mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 02:01:40 -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_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...";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -285,6 +285,7 @@ public:
|
|||
|
||||
Dialogs::IndexedList *contactsList();
|
||||
Dialogs::IndexedList *dialogsList();
|
||||
Dialogs::IndexedList *contactsNoDialogsList();
|
||||
|
||||
struct MessageToSend {
|
||||
History *history = nullptr;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -41,6 +41,11 @@ public:
|
|||
void showFast();
|
||||
void hideFast();
|
||||
|
||||
void finishAnimation() {
|
||||
_a_height.finish();
|
||||
animationCallback();
|
||||
}
|
||||
|
||||
TWidget *entity() {
|
||||
return _entity;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue