mirror of https://github.com/procxx/kepka.git
Add channel / supergroup admin event log filter.
This commit is contained in:
parent
fc6aa288c2
commit
0ae661edf0
|
@ -1322,8 +1322,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
"lng_admin_log_filter_messages_deleted" = "Deleted messages";
|
||||
"lng_admin_log_filter_messages_edited" = "Edited messages";
|
||||
"lng_admin_log_filter_messages_pinned" = "Pinned messages";
|
||||
"lng_admin_log_filter_members_removed" = "Members removed";
|
||||
"lng_admin_log_filter_all_admins" = "All admins";
|
||||
"lng_admin_log_filter_members_removed" = "Leaving members";
|
||||
"lng_admin_log_filter_all_admins" = "All users and admins";
|
||||
"lng_admin_log_about" = "What is this?";
|
||||
"lng_admin_log_about_text" = "This is a list of all service actions taken by the group's members and admins in the last 48 hours.";
|
||||
"lng_admin_log_no_results_title" = "No events found";
|
||||
|
|
|
@ -631,3 +631,17 @@ changePhoneLabel: FlatLabel(defaultFlatLabel) {
|
|||
changePhoneError: FlatLabel(changePhoneLabel) {
|
||||
textFg: boxTextFgError;
|
||||
}
|
||||
|
||||
adminLogFilterUserpicLeft: 15px;
|
||||
adminLogFilterLittleSkip: 16px;
|
||||
adminLogFilterCheckbox: Checkbox(defaultBoxCheckbox) {
|
||||
style: TextStyle(boxTextStyle) {
|
||||
font: font(boxFontSize semibold);
|
||||
linkFont: font(boxFontSize semibold);
|
||||
linkFontOver: font(boxFontSize semibold underline);
|
||||
}
|
||||
}
|
||||
adminLogFilterSkip: 32px;
|
||||
adminLogFilterUserCheckbox: Checkbox(defaultBoxCheckbox) {
|
||||
margin: margins(8px, 6px, 8px, 6px);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "history/history_admin_log_filter.h"
|
||||
|
||||
#include "styles/style_boxes.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
||||
namespace AdminLog {
|
||||
namespace {
|
||||
|
||||
class UserCheckbox : public Ui::RippleButton {
|
||||
public:
|
||||
UserCheckbox(QWidget *parent, gsl::not_null<UserData*> user, bool checked, base::lambda<void()> changedCallback);
|
||||
|
||||
bool checked() const {
|
||||
return _checked;
|
||||
}
|
||||
enum class NotifyAboutChange {
|
||||
Notify,
|
||||
DontNotify,
|
||||
};
|
||||
void setChecked(bool checked, NotifyAboutChange notify = NotifyAboutChange::Notify);
|
||||
|
||||
void finishAnimations();
|
||||
|
||||
QMargins getMargins() const override {
|
||||
return _st.margin;
|
||||
}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
QImage prepareRippleMask() const override;
|
||||
QPoint prepareRippleStartPosition() const override;
|
||||
|
||||
private:
|
||||
const style::Checkbox &_st;
|
||||
|
||||
QRect _checkRect;
|
||||
|
||||
bool _checked = false;
|
||||
Animation _a_checked;
|
||||
|
||||
gsl::not_null<UserData*> _user;
|
||||
base::lambda<void()> _changedCallback;
|
||||
QString _statusText;
|
||||
bool _statusOnline = false;
|
||||
|
||||
};
|
||||
|
||||
UserCheckbox::UserCheckbox(QWidget *parent, gsl::not_null<UserData*> user, bool checked, base::lambda<void()> changedCallback) : Ui::RippleButton(parent, st::defaultBoxCheckbox.ripple)
|
||||
, _st(st::adminLogFilterUserCheckbox)
|
||||
, _checked(checked)
|
||||
, _user(user)
|
||||
, _changedCallback(std::move(changedCallback)) {
|
||||
setCursor(style::cur_pointer);
|
||||
setClickedCallback([this] {
|
||||
if (isDisabled()) return;
|
||||
setChecked(!this->checked());
|
||||
});
|
||||
auto now = unixtime();
|
||||
_statusText = App::onlineText(_user, now);
|
||||
_statusOnline = App::onlineColorUse(_user, now);
|
||||
_checkRect = myrtlrect(_st.margin.left(), (st::contactsPhotoSize - _st.diameter) / 2, _st.diameter, _st.diameter);
|
||||
}
|
||||
|
||||
void UserCheckbox::setChecked(bool checked, NotifyAboutChange notify) {
|
||||
if (_checked != checked) {
|
||||
_checked = checked;
|
||||
_a_checked.start([this] { update(_checkRect); }, _checked ? 0. : 1., _checked ? 1. : 0., _st.duration);
|
||||
if (notify == NotifyAboutChange::Notify && _changedCallback) {
|
||||
_changedCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UserCheckbox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
auto ms = getms();
|
||||
auto active = _a_checked.current(ms, _checked ? 1. : 0.);
|
||||
auto color = anim::color(_st.rippleBg, _st.rippleBgActive, active);
|
||||
paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y() + (_checkRect.y() - st::defaultBoxCheckbox.margin.top()), ms, &color);
|
||||
|
||||
if (_checkRect.intersects(e->rect())) {
|
||||
auto pen = anim::pen(_st.checkFg, _st.checkFgActive, active);
|
||||
pen.setWidth(_st.thickness);
|
||||
p.setPen(pen);
|
||||
p.setBrush(anim::brush(_st.checkBg, anim::color(_st.checkFg, _st.checkFgActive, active), active));
|
||||
|
||||
{
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawRoundedRect(QRectF(_checkRect).marginsRemoved(QMarginsF(_st.thickness / 2., _st.thickness / 2., _st.thickness / 2., _st.thickness / 2.)), st::buttonRadius - (_st.thickness / 2.), st::buttonRadius - (_st.thickness / 2.));
|
||||
}
|
||||
|
||||
if (active > 0) {
|
||||
_st.checkIcon.paint(p, _checkRect.topLeft(), width());
|
||||
}
|
||||
}
|
||||
|
||||
auto userpicLeft = _checkRect.x() + _checkRect.width() + st::adminLogFilterUserpicLeft;
|
||||
auto userpicTop = 0;
|
||||
_user->paintUserpicLeft(p, userpicLeft, userpicTop, width(), st::contactsPhotoSize);
|
||||
|
||||
auto nameLeft = userpicLeft + st::contactsPhotoSize + st::contactsPadding.left();
|
||||
auto nameTop = userpicTop + st::contactsNameTop;
|
||||
auto nameWidth = width() - nameLeft - st::contactsPadding.right();
|
||||
p.setPen(st::contactsNameFg);
|
||||
_user->nameText.drawLeftElided(p, nameLeft, nameTop, nameWidth, width());
|
||||
|
||||
auto statusLeft = nameLeft;
|
||||
auto statusTop = userpicTop + st::contactsStatusTop;
|
||||
p.setFont(st::contactsStatusFont);
|
||||
p.setPen(_statusOnline ? st::contactsStatusFgOnline : st::contactsStatusFg);
|
||||
p.drawTextLeft(statusLeft, statusTop, width(), _statusText);
|
||||
}
|
||||
|
||||
void UserCheckbox::finishAnimations() {
|
||||
_a_checked.finish();
|
||||
}
|
||||
|
||||
int UserCheckbox::resizeGetHeight(int newWidth) {
|
||||
return st::contactsPhotoSize;
|
||||
}
|
||||
|
||||
QImage UserCheckbox::prepareRippleMask() const {
|
||||
return Ui::RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize));
|
||||
}
|
||||
|
||||
QPoint UserCheckbox::prepareRippleStartPosition() const {
|
||||
auto position = mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition - QPoint(0, _checkRect.y() - st::defaultBoxCheckbox.margin.top());
|
||||
if (QRect(0, 0, _st.rippleAreaSize, _st.rippleAreaSize).contains(position)) {
|
||||
return position;
|
||||
}
|
||||
return disabledRippleStartPosition();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class FilterBox::Inner : public TWidget {
|
||||
public:
|
||||
Inner(QWidget *parent, gsl::not_null<ChannelData*> channel, const std::vector<gsl::not_null<UserData*>> &admins, const FilterValue &filter, base::lambda<void()> changedCallback);
|
||||
|
||||
template <typename Widget>
|
||||
QPointer<Widget> addRow(object_ptr<Widget> widget, int marginTop) {
|
||||
widget->setParent(this);
|
||||
widget->show();
|
||||
auto row = Row();
|
||||
row.widget = std::move(widget);
|
||||
row.marginTop = marginTop;
|
||||
_rows.push_back(std::move(row));
|
||||
return static_cast<Widget*>(_rows.back().widget.data());
|
||||
}
|
||||
|
||||
bool canSave() const;
|
||||
FilterValue filter() const;
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
void createControls(const std::vector<gsl::not_null<UserData*>> &admins, const FilterValue &filter);
|
||||
void createAllActionsCheckbox(const FilterValue &filter);
|
||||
void createActionsCheckboxes(const FilterValue &filter);
|
||||
void createAllUsersCheckbox(const FilterValue &filter);
|
||||
void createAdminsCheckboxes(const std::vector<gsl::not_null<UserData*>> &admins, const FilterValue &filter);
|
||||
|
||||
gsl::not_null<ChannelData*> _channel;
|
||||
|
||||
QPointer<Ui::Checkbox> _allFlags;
|
||||
QMap<MTPDchannelAdminLogEventsFilter::Flags, QPointer<Ui::Checkbox>> _filterFlags;
|
||||
|
||||
QPointer<Ui::Checkbox> _allUsers;
|
||||
QMap<gsl::not_null<UserData*>, QPointer<UserCheckbox>> _admins;
|
||||
bool _restoringInvariant = false;
|
||||
|
||||
struct Row {
|
||||
object_ptr<TWidget> widget = { nullptr };
|
||||
int marginTop = 0;
|
||||
};
|
||||
std::vector<Row> _rows;
|
||||
|
||||
base::lambda<void()> _changedCallback;
|
||||
|
||||
};
|
||||
|
||||
FilterBox::Inner::Inner(QWidget *parent, gsl::not_null<ChannelData*> channel, const std::vector<gsl::not_null<UserData*>> &admins, const FilterValue &filter, base::lambda<void()> changedCallback) : TWidget(parent)
|
||||
, _channel(channel)
|
||||
, _changedCallback(std::move(changedCallback)) {
|
||||
createControls(admins, filter);
|
||||
}
|
||||
|
||||
void FilterBox::Inner::createControls(const std::vector<gsl::not_null<UserData*>> &admins, const FilterValue &filter) {
|
||||
createAllActionsCheckbox(filter);
|
||||
createActionsCheckboxes(filter);
|
||||
createAllUsersCheckbox(filter);
|
||||
createAdminsCheckboxes(admins, filter);
|
||||
}
|
||||
|
||||
void FilterBox::Inner::createAllActionsCheckbox(const FilterValue &filter) {
|
||||
auto checked = (filter.flags == 0);
|
||||
_allFlags = addRow(object_ptr<Ui::Checkbox>(this, lang(lng_admin_log_filter_all_actions), checked, st::adminLogFilterCheckbox), st::adminLogFilterCheckbox.margin.top());
|
||||
connect(_allFlags, &Ui::Checkbox::changed, this, [this] {
|
||||
if (!std::exchange(_restoringInvariant, true)) {
|
||||
auto allChecked = _allFlags->checked();
|
||||
for_const (auto &&checkbox, _filterFlags) {
|
||||
checkbox->setChecked(allChecked);
|
||||
}
|
||||
_restoringInvariant = false;
|
||||
if (_changedCallback) {
|
||||
_changedCallback();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void FilterBox::Inner::createActionsCheckboxes(const FilterValue &filter) {
|
||||
using Flag = MTPDchannelAdminLogEventsFilter::Flag;
|
||||
using Flags = MTPDchannelAdminLogEventsFilter::Flags;
|
||||
auto addFlag = [this, &filter](Flags flag, QString &&text) {
|
||||
auto checked = (filter.flags == 0) || (filter.flags & flag);
|
||||
auto checkbox = addRow(object_ptr<Ui::Checkbox>(this, std::move(text), checked, st::defaultBoxCheckbox), st::adminLogFilterLittleSkip);
|
||||
_filterFlags.insert(flag, checkbox);
|
||||
connect(checkbox, &Ui::Checkbox::changed, this, [this] {
|
||||
if (!std::exchange(_restoringInvariant, true)) {
|
||||
auto allChecked = true;
|
||||
for_const (auto &&checkbox, _filterFlags) {
|
||||
if (!checkbox->checked()) {
|
||||
allChecked = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_allFlags->setChecked(allChecked);
|
||||
_restoringInvariant = false;
|
||||
if (_changedCallback) {
|
||||
_changedCallback();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
auto isGroup = _channel->isMegagroup();
|
||||
if (isGroup) {
|
||||
addFlag(Flag::f_ban | Flag::f_unban | Flag::f_kick | Flag::f_unkick, lang(lng_admin_log_filter_restrictions));
|
||||
}
|
||||
addFlag(Flag::f_promote | Flag::f_demote, lang(lng_admin_log_filter_admins_new));
|
||||
addFlag(Flag::f_join | Flag::f_invite, lang(lng_admin_log_filter_members_new));
|
||||
addFlag(Flag::f_info | Flag::f_settings, lang(_channel->isMegagroup() ? lng_admin_log_filter_info_group : lng_admin_log_filter_info_channel));
|
||||
addFlag(Flag::f_delete, lang(lng_admin_log_filter_messages_deleted));
|
||||
addFlag(Flag::f_edit, lang(lng_admin_log_filter_messages_edited));
|
||||
if (isGroup) {
|
||||
addFlag(Flag::f_pinned, lang(lng_admin_log_filter_messages_pinned));
|
||||
}
|
||||
addFlag(Flag::f_leave, lang(lng_admin_log_filter_members_removed));
|
||||
}
|
||||
|
||||
void FilterBox::Inner::createAllUsersCheckbox(const FilterValue &filter) {
|
||||
_allUsers = addRow(object_ptr<Ui::Checkbox>(this, lang(lng_admin_log_filter_all_admins), filter.allUsers, st::adminLogFilterCheckbox), st::adminLogFilterSkip);
|
||||
connect(_allUsers, &Ui::Checkbox::changed, this, [this] {
|
||||
if (_allUsers->checked() && !std::exchange(_restoringInvariant, true)) {
|
||||
for_const (auto &&checkbox, _admins) {
|
||||
checkbox->setChecked(true);
|
||||
}
|
||||
_restoringInvariant = false;
|
||||
if (_changedCallback) {
|
||||
_changedCallback();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void FilterBox::Inner::createAdminsCheckboxes(const std::vector<gsl::not_null<UserData*>> &admins, const FilterValue &filter) {
|
||||
for (auto user : admins) {
|
||||
auto checked = filter.allUsers || base::contains(filter.admins, user);
|
||||
auto checkbox = addRow(object_ptr<UserCheckbox>(this, user, checked, [this] {
|
||||
if (!std::exchange(_restoringInvariant, true)) {
|
||||
auto allChecked = true;
|
||||
for_const (auto &&checkbox, _admins) {
|
||||
if (!checkbox->checked()) {
|
||||
allChecked = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allChecked) {
|
||||
_allUsers->setChecked(allChecked);
|
||||
}
|
||||
_restoringInvariant = false;
|
||||
if (_changedCallback) {
|
||||
_changedCallback();
|
||||
}
|
||||
}
|
||||
}), st::adminLogFilterLittleSkip);
|
||||
_admins.insert(user, checkbox);
|
||||
}
|
||||
}
|
||||
|
||||
bool FilterBox::Inner::canSave() const {
|
||||
for (auto i = _filterFlags.cbegin(), e = _filterFlags.cend(); i != e; ++i) {
|
||||
if (i.value()->checked()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FilterValue FilterBox::Inner::filter() const {
|
||||
auto result = FilterValue();
|
||||
auto allChecked = true;
|
||||
for (auto i = _filterFlags.cbegin(), e = _filterFlags.cend(); i != e; ++i) {
|
||||
if (i.value()->checked()) {
|
||||
result.flags |= i.key();
|
||||
} else {
|
||||
allChecked = false;
|
||||
}
|
||||
}
|
||||
if (allChecked) {
|
||||
result.flags = 0;
|
||||
}
|
||||
result.allUsers = _allUsers->checked();
|
||||
if (!result.allUsers) {
|
||||
result.admins.reserve(_admins.size());
|
||||
for (auto i = _admins.cbegin(), e = _admins.cend(); i != e; ++i) {
|
||||
if (i.value()->checked()) {
|
||||
result.admins.push_back(i.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int FilterBox::Inner::resizeGetHeight(int newWidth) {
|
||||
auto newHeight = 0;
|
||||
auto rowWidth = newWidth - st::boxPadding.left() - st::boxPadding.right();
|
||||
for (auto &&row : _rows) {
|
||||
newHeight += row.marginTop;
|
||||
row.widget->resizeToNaturalWidth(rowWidth);
|
||||
newHeight += row.widget->heightNoMargins();
|
||||
}
|
||||
return newHeight;
|
||||
}
|
||||
|
||||
void FilterBox::Inner::resizeEvent(QResizeEvent *e) {
|
||||
auto top = 0;
|
||||
for (auto &&row : _rows) {
|
||||
top += row.marginTop;
|
||||
row.widget->moveToLeft(st::boxPadding.left(), top);
|
||||
top += row.widget->heightNoMargins();
|
||||
}
|
||||
}
|
||||
|
||||
FilterBox::FilterBox(QWidget*, gsl::not_null<ChannelData*> channel, const std::vector<gsl::not_null<UserData*>> &admins, const FilterValue &filter, base::lambda<void(FilterValue &&filter)> saveCallback) : BoxContent()
|
||||
, _channel(channel)
|
||||
, _admins(admins)
|
||||
, _initialFilter(filter)
|
||||
, _saveCallback(std::move(saveCallback)) {
|
||||
}
|
||||
|
||||
void FilterBox::prepare() {
|
||||
setTitle(langFactory(lng_admin_log_filter_title));
|
||||
|
||||
_inner = setInnerWidget(object_ptr<Inner>(this, _channel, _admins, _initialFilter, [this] { refreshButtons(); }));
|
||||
_inner->resizeToWidth(st::boxWideWidth);
|
||||
|
||||
refreshButtons();
|
||||
setDimensions(st::boxWideWidth, _inner->height());
|
||||
}
|
||||
|
||||
void FilterBox::refreshButtons() {
|
||||
clearButtons();
|
||||
if (_inner->canSave()) {
|
||||
addButton(langFactory(lng_settings_save), [this] {
|
||||
if (_saveCallback) {
|
||||
_saveCallback(_inner->filter());
|
||||
}
|
||||
});
|
||||
}
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
}
|
||||
|
||||
template <typename Widget>
|
||||
QPointer<Widget> FilterBox::addControl(object_ptr<Widget> row) {
|
||||
Expects(_inner != nullptr);
|
||||
return _inner->addControl(std::move(row));
|
||||
}
|
||||
|
||||
void FilterBox::resizeToContent() {
|
||||
_inner->resizeToWidth(st::boxWideWidth);
|
||||
setDimensions(_inner->width(), _inner->height());
|
||||
}
|
||||
|
||||
} // namespace AdminLog
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "history/history_admin_log_section.h"
|
||||
|
||||
namespace AdminLog {
|
||||
|
||||
class FilterBox : public BoxContent {
|
||||
public:
|
||||
FilterBox(QWidget*, gsl::not_null<ChannelData*> channel, const std::vector<gsl::not_null<UserData*>> &admins, const FilterValue &filter, base::lambda<void(FilterValue &&filter)> saveCallback);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
private:
|
||||
void resizeToContent();
|
||||
void refreshButtons();
|
||||
|
||||
gsl::not_null<ChannelData*> _channel;
|
||||
std::vector<gsl::not_null<UserData*>> _admins;
|
||||
FilterValue _initialFilter;
|
||||
base::lambda<void(FilterValue &&filter)> _saveCallback;
|
||||
|
||||
class Inner;
|
||||
QPointer<Inner> _inner;
|
||||
|
||||
};
|
||||
|
||||
} // namespace AdminLog
|
|
@ -312,16 +312,21 @@ void InnerWidget::checkPreloadMore() {
|
|||
}
|
||||
}
|
||||
|
||||
void InnerWidget::applyFilter(MTPDchannelAdminLogEventsFilter::Flags flags, const std::vector<gsl::not_null<UserData*>> &admins) {
|
||||
_filterFlags = flags;
|
||||
_filterAdmins = admins;
|
||||
updateEmptyText();
|
||||
void InnerWidget::applyFilter(FilterValue &&value) {
|
||||
_filter = value;
|
||||
request(base::take(_preloadUpRequestId)).cancel();
|
||||
request(base::take(_preloadDownRequestId)).cancel();
|
||||
_filterChanged = true;
|
||||
_upLoaded = false;
|
||||
_downLoaded = true;
|
||||
updateMinMaxIds();
|
||||
preloadMore(Direction::Up);
|
||||
}
|
||||
|
||||
void InnerWidget::updateEmptyText() {
|
||||
auto options = _defaultOptions;
|
||||
options.flags |= TextParseMono; // For italic :/
|
||||
auto hasFilter = (_filterFlags != 0) || !_filterAdmins.empty();
|
||||
auto hasFilter = (_filter.flags != 0) || !_filter.allUsers;
|
||||
auto text = TextWithEntities { lang(hasFilter ? lng_admin_log_no_results_title : lng_admin_log_no_events_title) };
|
||||
text.entities.append(EntityInText(EntityInTextBold, 0, text.text.size()));
|
||||
text.text.append(qstr("\n\n") + lang(hasFilter ? lng_admin_log_no_results_text : lng_admin_log_no_events_text));
|
||||
|
@ -351,8 +356,11 @@ QPoint InnerWidget::tooltipPos() const {
|
|||
}
|
||||
|
||||
void InnerWidget::saveState(gsl::not_null<SectionMemento*> memento) {
|
||||
memento->setItems(std::move(_items), std::move(_itemsByIds), _upLoaded, _downLoaded);
|
||||
memento->setIdManager(std::move(_idManager));
|
||||
memento->setFilter(std::move(_filter));
|
||||
if (!_filterChanged) {
|
||||
memento->setItems(std::move(_items), std::move(_itemsByIds), _upLoaded, _downLoaded);
|
||||
memento->setIdManager(std::move(_idManager));
|
||||
}
|
||||
_upLoaded = _downLoaded = true; // Don't load or handle anything anymore.
|
||||
}
|
||||
|
||||
|
@ -360,8 +368,10 @@ void InnerWidget::restoreState(gsl::not_null<SectionMemento*> memento) {
|
|||
_items = memento->takeItems();
|
||||
_itemsByIds = memento->takeItemsByIds();
|
||||
_idManager = memento->takeIdManager();
|
||||
_filter = memento->takeFilter();
|
||||
_upLoaded = memento->upLoaded();
|
||||
_downLoaded = memento->downLoaded();
|
||||
_filterChanged = false;
|
||||
updateMinMaxIds();
|
||||
updateSize();
|
||||
}
|
||||
|
@ -374,15 +384,17 @@ void InnerWidget::preloadMore(Direction direction) {
|
|||
}
|
||||
|
||||
auto flags = MTPchannels_GetAdminLog::Flags(0);
|
||||
auto filter = MTP_channelAdminLogEventsFilter(MTP_flags(_filterFlags));
|
||||
if (_filterFlags != 0) {
|
||||
auto filter = MTP_channelAdminLogEventsFilter(MTP_flags(_filter.flags));
|
||||
if (_filter.flags != 0) {
|
||||
flags |= MTPchannels_GetAdminLog::Flag::f_events_filter;
|
||||
}
|
||||
auto admins = QVector<MTPInputUser>(0);
|
||||
if (!_filterAdmins.empty()) {
|
||||
admins.reserve(_filterAdmins.size());
|
||||
for (auto &admin : _filterAdmins) {
|
||||
admins.push_back(admin->inputUser);
|
||||
if (!_filter.allUsers) {
|
||||
if (!_filter.admins.empty()) {
|
||||
admins.reserve(_filter.admins.size());
|
||||
for (auto &admin : _filter.admins) {
|
||||
admins.push_back(admin->inputUser);
|
||||
}
|
||||
}
|
||||
flags |= MTPchannels_GetAdminLog::Flag::f_admins;
|
||||
}
|
||||
|
@ -401,6 +413,11 @@ void InnerWidget::preloadMore(Direction direction) {
|
|||
if (loadedFlag) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_filterChanged) {
|
||||
clearAfterFilterChange();
|
||||
}
|
||||
|
||||
auto &events = results.vevents.v;
|
||||
if (!events.empty()) {
|
||||
auto oldItemsCount = _items.size();
|
||||
|
@ -459,7 +476,7 @@ void InnerWidget::preloadMore(Direction direction) {
|
|||
}
|
||||
|
||||
void InnerWidget::updateMinMaxIds() {
|
||||
if (_itemsByIds.empty()) {
|
||||
if (_itemsByIds.empty() || _filterChanged) {
|
||||
_maxId = _minId = 0;
|
||||
} else {
|
||||
_maxId = (--_itemsByIds.end())->first;
|
||||
|
@ -585,6 +602,22 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
|||
}
|
||||
}
|
||||
|
||||
void InnerWidget::clearAfterFilterChange() {
|
||||
_visibleTopItem = nullptr;
|
||||
_visibleTopFromItem = 0;
|
||||
_scrollDateLastItem = nullptr;
|
||||
_scrollDateLastItemTop = 0;
|
||||
_mouseActionItem = nullptr;
|
||||
_selectedItem = nullptr;
|
||||
_selectedText = TextSelection();
|
||||
_filterChanged = false;
|
||||
_items.clear();
|
||||
_itemsByIds.clear();
|
||||
_idManager = LocalIdManager();
|
||||
updateEmptyText();
|
||||
updateSize();
|
||||
}
|
||||
|
||||
void InnerWidget::paintEmpty(Painter &p) {
|
||||
style::font font(st::msgServiceFont);
|
||||
auto rectWidth = st::historyAdminLogEmptyWidth;
|
||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#pragma once
|
||||
|
||||
#include "history/history_admin_log_item.h"
|
||||
#include "history/history_admin_log_section.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "base/timer.h"
|
||||
|
@ -37,26 +38,6 @@ namespace AdminLog {
|
|||
|
||||
class SectionMemento;
|
||||
|
||||
class LocalIdManager {
|
||||
public:
|
||||
LocalIdManager() = default;
|
||||
LocalIdManager(const LocalIdManager &other) = delete;
|
||||
LocalIdManager &operator=(const LocalIdManager &other) = delete;
|
||||
LocalIdManager(LocalIdManager &&other) : _counter(std::exchange(other._counter, ServerMaxMsgId)) {
|
||||
}
|
||||
LocalIdManager &operator=(LocalIdManager &&other) {
|
||||
_counter = std::exchange(other._counter, ServerMaxMsgId);
|
||||
return *this;
|
||||
}
|
||||
MsgId next() {
|
||||
return ++_counter;
|
||||
}
|
||||
|
||||
private:
|
||||
MsgId _counter = ServerMaxMsgId;
|
||||
|
||||
};
|
||||
|
||||
class InnerWidget final : public TWidget, public Ui::AbstractTooltipShower, private MTP::Sender, private base::Subscriber {
|
||||
public:
|
||||
InnerWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller, gsl::not_null<ChannelData*> channel, base::lambda<void(int top)> scrollTo);
|
||||
|
@ -82,8 +63,11 @@ public:
|
|||
_cancelledCallback = std::move(callback);
|
||||
}
|
||||
|
||||
// Empty "flags" means all events. Empty "admins" means all admins.
|
||||
void applyFilter(MTPDchannelAdminLogEventsFilter::Flags flags, const std::vector<gsl::not_null<UserData*>> &admins);
|
||||
// Empty "flags" means all events.
|
||||
void applyFilter(FilterValue &&value);
|
||||
FilterValue filter() const {
|
||||
return _filter;
|
||||
}
|
||||
|
||||
// AbstractTooltipShower interface
|
||||
QString tooltipText() const override;
|
||||
|
@ -154,6 +138,7 @@ private:
|
|||
void updateMinMaxIds();
|
||||
void updateEmptyText();
|
||||
void paintEmpty(Painter &p);
|
||||
void clearAfterFilterChange();
|
||||
|
||||
void toggleScrollDateShown();
|
||||
void repaintScrollDateCallback();
|
||||
|
@ -219,6 +204,7 @@ private:
|
|||
// Don't load anything until the memento was read.
|
||||
bool _upLoaded = true;
|
||||
bool _downLoaded = true;
|
||||
bool _filterChanged = false;
|
||||
Text _emptyText;
|
||||
|
||||
MouseAction _mouseAction = MouseAction::None;
|
||||
|
@ -243,8 +229,7 @@ private:
|
|||
|
||||
ClickHandlerPtr _contextMenuLink;
|
||||
|
||||
MTPDchannelAdminLogEventsFilter::Flags _filterFlags = 0;
|
||||
std::vector<gsl::not_null<UserData*>> _filterAdmins;
|
||||
FilterValue _filter;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "history/history_admin_log_section.h"
|
||||
|
||||
#include "history/history_admin_log_inner.h"
|
||||
#include "history/history_admin_log_filter.h"
|
||||
#include "profile/profile_back_button.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_window.h"
|
||||
|
@ -36,16 +37,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
|
||||
namespace AdminLog {
|
||||
|
||||
// If we require to support more admins we'll have to rewrite this anyway.
|
||||
constexpr auto kMaxChannelAdmins = 200;
|
||||
|
||||
class FixedBar final : public TWidget, private base::Subscriber {
|
||||
public:
|
||||
FixedBar(QWidget *parent);
|
||||
FixedBar(QWidget *parent, gsl::not_null<ChannelData*> channel, base::lambda<void()> showFilterCallback);
|
||||
|
||||
// When animating mode is enabled the content is hidden and the
|
||||
// whole fixed bar acts like a back button.
|
||||
void setAnimatingMode(bool enabled);
|
||||
|
||||
// Empty "flags" means all events. Empty "admins" means all admins.
|
||||
void applyFilter(MTPDchannelAdminLogEventsFilter::Flags flags, const std::vector<gsl::not_null<UserData*>> &admins);
|
||||
void applyFilter(const FilterValue &value);
|
||||
void goBack();
|
||||
|
||||
protected:
|
||||
|
@ -54,6 +57,7 @@ protected:
|
|||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
private:
|
||||
gsl::not_null<ChannelData*> _channel;
|
||||
object_ptr<Profile::BackButton> _backButton;
|
||||
object_ptr<Ui::RoundButton> _filter;
|
||||
|
||||
|
@ -67,17 +71,17 @@ object_ptr<Window::SectionWidget> SectionMemento::createWidget(QWidget *parent,
|
|||
return std::move(result);
|
||||
}
|
||||
|
||||
FixedBar::FixedBar(QWidget *parent) : TWidget(parent)
|
||||
FixedBar::FixedBar(QWidget *parent, gsl::not_null<ChannelData*> channel, base::lambda<void()> showFilterCallback) : TWidget(parent)
|
||||
, _channel(channel)
|
||||
, _backButton(this, lang(lng_admin_log_title_all))
|
||||
, _filter(this, langFactory(lng_admin_log_filter), st::topBarButton) {
|
||||
_backButton->moveToLeft(0, 0);
|
||||
_backButton->setClickedCallback([this] { goBack(); });
|
||||
_filter->setClickedCallback([this] {});
|
||||
_filter->hide();
|
||||
_filter->setClickedCallback([this, showFilterCallback] { showFilterCallback(); });
|
||||
}
|
||||
|
||||
void FixedBar::applyFilter(MTPDchannelAdminLogEventsFilter::Flags flags, const std::vector<gsl::not_null<UserData*>> &admins) {
|
||||
auto hasFilter = (flags != 0) || !admins.empty();
|
||||
void FixedBar::applyFilter(const FilterValue &value) {
|
||||
auto hasFilter = (value.flags != 0) || !value.allUsers;
|
||||
_backButton->setText(lang(hasFilter ? lng_admin_log_title_selected : lng_admin_log_title_all));
|
||||
}
|
||||
|
||||
|
@ -89,7 +93,7 @@ int FixedBar::resizeGetHeight(int newWidth) {
|
|||
auto newHeight = 0;
|
||||
|
||||
auto buttonLeft = newWidth;
|
||||
//buttonLeft -= _filter->width(); _filter->moveToLeft(buttonLeft, 0);
|
||||
buttonLeft -= _filter->width(); _filter->moveToLeft(buttonLeft, 0);
|
||||
_backButton->resizeToWidth(buttonLeft);
|
||||
_backButton->moveToLeft(0, 0);
|
||||
newHeight += _backButton->height();
|
||||
|
@ -107,7 +111,6 @@ void FixedBar::setAnimatingMode(bool enabled) {
|
|||
} else {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
showChildren();
|
||||
_filter->hide();
|
||||
}
|
||||
show();
|
||||
}
|
||||
|
@ -130,7 +133,7 @@ void FixedBar::mousePressEvent(QMouseEvent *e) {
|
|||
|
||||
Widget::Widget(QWidget *parent, gsl::not_null<Window::Controller*> controller, gsl::not_null<ChannelData*> channel) : Window::SectionWidget(parent, controller)
|
||||
, _scroll(this, st::historyScroll, false)
|
||||
, _fixedBar(this)
|
||||
, _fixedBar(this, channel, [this] { showFilter(); })
|
||||
, _fixedBarShadow(this, st::shadowFg)
|
||||
, _whatIsThis(this, lang(lng_admin_log_about).toUpper(), st::historyComposeButton) {
|
||||
_fixedBar->move(0, 0);
|
||||
|
@ -151,6 +154,40 @@ Widget::Widget(QWidget *parent, gsl::not_null<Window::Controller*> controller, g
|
|||
_whatIsThis->setClickedCallback([this] { Ui::show(Box<InformBox>(lang(lng_admin_log_about_text))); });
|
||||
}
|
||||
|
||||
void Widget::showFilter() {
|
||||
if (_admins.empty()) {
|
||||
request(MTPchannels_GetParticipants(_inner->channel()->inputChannel, MTP_channelParticipantsAdmins(), MTP_int(0), MTP_int(kMaxChannelAdmins))).done([this](const MTPchannels_ChannelParticipants &result) {
|
||||
Expects(result.type() == mtpc_channels_channelParticipants);
|
||||
auto &participants = result.c_channels_channelParticipants();
|
||||
App::feedUsers(participants.vusers);
|
||||
for (auto &participant : participants.vparticipants.v) {
|
||||
auto getUserId = [&participant] {
|
||||
switch (participant.type()) {
|
||||
case mtpc_channelParticipant: return participant.c_channelParticipant().vuser_id.v;
|
||||
case mtpc_channelParticipantSelf: return participant.c_channelParticipantSelf().vuser_id.v;
|
||||
case mtpc_channelParticipantAdmin: return participant.c_channelParticipantAdmin().vuser_id.v;
|
||||
case mtpc_channelParticipantCreator: return participant.c_channelParticipantCreator().vuser_id.v;
|
||||
case mtpc_channelParticipantBanned: return participant.c_channelParticipantBanned().vuser_id.v;
|
||||
default: Unexpected("Type in AdminLog::Widget::showFilter()");
|
||||
}
|
||||
};
|
||||
if (auto user = App::userLoaded(getUserId())) {
|
||||
_admins.push_back(user);
|
||||
}
|
||||
}
|
||||
if (_admins.empty()) {
|
||||
_admins.push_back(App::self());
|
||||
}
|
||||
showFilter();
|
||||
}).send();
|
||||
} else {
|
||||
Ui::show(Box<FilterBox>(_inner->channel(), _admins, _inner->filter(), [this](FilterValue &&filter) {
|
||||
applyFilter(std::move(filter));
|
||||
Ui::hideLayer();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::updateAdaptiveLayout() {
|
||||
_fixedBarShadow->moveToLeft(Adaptive::OneColumn() ? 0 : st::lineWidth, _fixedBar->height());
|
||||
}
|
||||
|
@ -194,11 +231,13 @@ std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
|||
|
||||
void Widget::saveState(gsl::not_null<SectionMemento*> memento) {
|
||||
memento->setScrollTop(_scroll->scrollTop());
|
||||
memento->setAdmins(std::move(_admins));
|
||||
_inner->saveState(memento);
|
||||
}
|
||||
|
||||
void Widget::restoreState(gsl::not_null<SectionMemento*> memento) {
|
||||
_inner->restoreState(memento);
|
||||
_admins = memento->takeAdmins();
|
||||
auto scrollTop = memento->getScrollTop();
|
||||
_scroll->scrollToY(scrollTop);
|
||||
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
||||
|
@ -309,9 +348,9 @@ QRect Widget::rectForFloatPlayer(Window::Column myColumn, Window::Column playerC
|
|||
return mapToGlobal(_scroll->geometry());
|
||||
}
|
||||
|
||||
void Widget::applyFilter(MTPDchannelAdminLogEventsFilter::Flags flags, const std::vector<gsl::not_null<UserData*>> &admins) {
|
||||
_inner->applyFilter(flags, admins);
|
||||
_fixedBar->applyFilter(flags, admins);
|
||||
void Widget::applyFilter(FilterValue &&value) {
|
||||
_fixedBar->applyFilter(value);
|
||||
_inner->applyFilter(std::move(value));
|
||||
}
|
||||
|
||||
} // namespace AdminLog
|
||||
|
|
|
@ -23,7 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "window/section_widget.h"
|
||||
#include "window/section_memento.h"
|
||||
#include "history/history_admin_log_item.h"
|
||||
#include "history/history_admin_log_inner.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
namespace Notify {
|
||||
struct PeerUpdate;
|
||||
|
@ -45,7 +45,34 @@ class FixedBar;
|
|||
class InnerWidget;
|
||||
class SectionMemento;
|
||||
|
||||
class Widget final : public Window::SectionWidget {
|
||||
struct FilterValue {
|
||||
// Empty "flags" means all events.
|
||||
MTPDchannelAdminLogEventsFilter::Flags flags = 0;
|
||||
std::vector<gsl::not_null<UserData*>> admins;
|
||||
bool allUsers = true;
|
||||
};
|
||||
|
||||
class LocalIdManager {
|
||||
public:
|
||||
LocalIdManager() = default;
|
||||
LocalIdManager(const LocalIdManager &other) = delete;
|
||||
LocalIdManager &operator=(const LocalIdManager &other) = delete;
|
||||
LocalIdManager(LocalIdManager &&other) : _counter(std::exchange(other._counter, ServerMaxMsgId)) {
|
||||
}
|
||||
LocalIdManager &operator=(LocalIdManager &&other) {
|
||||
_counter = std::exchange(other._counter, ServerMaxMsgId);
|
||||
return *this;
|
||||
}
|
||||
MsgId next() {
|
||||
return ++_counter;
|
||||
}
|
||||
|
||||
private:
|
||||
MsgId _counter = ServerMaxMsgId;
|
||||
|
||||
};
|
||||
|
||||
class Widget final : public Window::SectionWidget, private MTP::Sender {
|
||||
public:
|
||||
Widget(QWidget *parent, gsl::not_null<Window::Controller*> controller, gsl::not_null<ChannelData*> channel);
|
||||
|
||||
|
@ -69,8 +96,7 @@ public:
|
|||
bool wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) override;
|
||||
QRect rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) override;
|
||||
|
||||
// Empty "flags" means all events. Empty "admins" means all admins.
|
||||
void applyFilter(MTPDchannelAdminLogEventsFilter::Flags flags, const std::vector<gsl::not_null<UserData*>> &admins);
|
||||
void applyFilter(FilterValue &&value);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
@ -81,6 +107,7 @@ protected:
|
|||
void doSetInnerFocus() override;
|
||||
|
||||
private:
|
||||
void showFilter();
|
||||
void onScroll();
|
||||
void updateAdaptiveLayout();
|
||||
void saveState(gsl::not_null<SectionMemento*> memento);
|
||||
|
@ -91,6 +118,7 @@ private:
|
|||
object_ptr<FixedBar> _fixedBar;
|
||||
object_ptr<Ui::PlainShadow> _fixedBarShadow;
|
||||
object_ptr<Ui::FlatButton> _whatIsThis;
|
||||
std::vector<gsl::not_null<UserData*>> _admins;
|
||||
|
||||
};
|
||||
|
||||
|
@ -111,12 +139,22 @@ public:
|
|||
return _scrollTop;
|
||||
}
|
||||
|
||||
void setAdmins(std::vector<gsl::not_null<UserData*>> admins) {
|
||||
_admins = std::move(admins);
|
||||
}
|
||||
std::vector<gsl::not_null<UserData*>> takeAdmins() {
|
||||
return std::move(_admins);
|
||||
}
|
||||
|
||||
void setItems(std::vector<HistoryItemOwned> &&items, std::map<uint64, HistoryItem*> &&itemsByIds, bool upLoaded, bool downLoaded) {
|
||||
_items = std::move(items);
|
||||
_itemsByIds = std::move(itemsByIds);
|
||||
_upLoaded = upLoaded;
|
||||
_downLoaded = downLoaded;
|
||||
}
|
||||
void setFilter(FilterValue &&filter) {
|
||||
_filter = std::move(filter);
|
||||
}
|
||||
void setIdManager(LocalIdManager &&manager) {
|
||||
_idManager = std::move(manager);
|
||||
}
|
||||
|
@ -135,15 +173,20 @@ public:
|
|||
bool downLoaded() const {
|
||||
return _downLoaded;
|
||||
}
|
||||
FilterValue takeFilter() {
|
||||
return std::move(_filter);
|
||||
}
|
||||
|
||||
private:
|
||||
gsl::not_null<ChannelData*> _channel;
|
||||
int _scrollTop = 0;
|
||||
std::vector<gsl::not_null<UserData*>> _admins;
|
||||
std::vector<HistoryItemOwned> _items;
|
||||
std::map<uint64, HistoryItem*> _itemsByIds;
|
||||
bool _upLoaded = false;
|
||||
bool _downLoaded = true;
|
||||
LocalIdManager _idManager;
|
||||
FilterValue _filter;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -143,6 +143,8 @@
|
|||
<(src_loc)/dialogs/dialogs_row.h
|
||||
<(src_loc)/history/history.cpp
|
||||
<(src_loc)/history/history.h
|
||||
<(src_loc)/history/history_admin_log_filter.cpp
|
||||
<(src_loc)/history/history_admin_log_filter.h
|
||||
<(src_loc)/history/history_admin_log_inner.cpp
|
||||
<(src_loc)/history/history_admin_log_inner.h
|
||||
<(src_loc)/history/history_admin_log_item.cpp
|
||||
|
|
Loading…
Reference in New Issue