Allow to ban from actions log user context menu.

This commit is contained in:
John Preston 2017-07-05 21:11:31 +03:00
parent 1a7353fb43
commit 0c43aabfec
9 changed files with 266 additions and 128 deletions

View File

@ -25,18 +25,23 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "history/history_message.h"
#include "history/history_service_layout.h"
#include "history/history_admin_log_section.h"
#include "history/history_admin_log_filter.h"
#include "chat_helpers/message_field.h"
#include "mainwindow.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "window/window_controller.h"
#include "auth_session.h"
#include "ui/widgets/popup_menu.h"
#include "core/file_utilities.h"
#include "lang/lang_keys.h"
#include "boxes/edit_participant_box.h"
namespace AdminLog {
namespace {
// If we require to support more admins we'll have to rewrite this anyway.
constexpr auto kMaxChannelAdmins = 200;
constexpr auto kScrollDateHideTimeout = 1000;
constexpr auto kEventsFirstPage = 20;
constexpr auto kEventsPerPage = 50;
@ -223,6 +228,8 @@ InnerWidget::InnerWidget(QWidget *parent, gsl::not_null<Window::Controller*> con
}
});
updateEmptyText();
requestAdmins();
}
void InnerWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) {
@ -326,6 +333,47 @@ void InnerWidget::applySearch(const QString &query) {
}
}
void InnerWidget::requestAdmins() {
request(MTPchannels_GetParticipants(_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);
auto canEdit = (participant.type() == mtpc_channelParticipantAdmin) && (participant.c_channelParticipantAdmin().is_can_edit());
if (canEdit) {
_adminsCanEdit.push_back(user);
}
}
}
if (_admins.empty()) {
_admins.push_back(App::self());
}
if (_showFilterCallback) {
showFilter(std::move(_showFilterCallback));
}
}).send();
}
void InnerWidget::showFilter(base::lambda<void(FilterValue &&filter)> callback) {
if (_admins.empty()) {
_showFilterCallback = std::move(callback);
} else {
Ui::show(Box<FilterBox>(_channel, _admins, _filter, std::move(callback)));
}
}
void InnerWidget::clearAndRequestLog() {
request(base::take(_preloadUpRequestId)).cancel();
request(base::take(_preloadDownRequestId)).cancel();
@ -374,6 +422,8 @@ QPoint InnerWidget::tooltipPos() const {
void InnerWidget::saveState(gsl::not_null<SectionMemento*> memento) {
memento->setFilter(std::move(_filter));
memento->setAdmins(std::move(_admins));
memento->setAdminsCanEdit(std::move(_adminsCanEdit));
memento->setSearchQuery(std::move(_searchQuery));
if (!_filterChanged) {
memento->setItems(std::move(_items), std::move(_itemsByIds), _upLoaded, _downLoaded);
@ -386,6 +436,8 @@ void InnerWidget::restoreState(gsl::not_null<SectionMemento*> memento) {
_items = memento->takeItems();
_itemsByIds = memento->takeItemsByIds();
_idManager = memento->takeIdManager();
_admins = memento->takeAdmins();
_adminsCanEdit = memento->takeAdminsCanEdit();
_filter = memento->takeFilter();
_searchQuery = memento->takeSearchQuery();
_upLoaded = memento->upLoaded();
@ -427,65 +479,9 @@ void InnerWidget::preloadMore(Direction direction) {
auto &results = result.c_channels_adminLogResults();
App::feedUsers(results.vusers);
App::feedChats(results.vchats);
if (loadedFlag) {
return;
if (!loadedFlag) {
addEvents(direction, results.vevents.v);
}
if (_filterChanged) {
clearAfterFilterChange();
}
auto &events = results.vevents.v;
if (!events.empty()) {
auto oldItemsCount = _items.size();
_items.reserve(oldItemsCount + events.size() * 2);
for_const (auto &event, events) {
t_assert(event.type() == mtpc_channelAdminLogEvent);
auto &data = event.c_channelAdminLogEvent();
if (_itemsByIds.find(data.vid.v) != _itemsByIds.cend()) {
continue;
}
auto count = 0;
GenerateItems(_history, _idManager, data, [this, id = data.vid.v, &count](HistoryItemOwned item) {
_itemsByIds.emplace(id, item.get());
_items.push_back(std::move(item));
++count;
});
if (count > 1) {
// Reverse the inner order of the added messages, because we load events
// from bottom to top but inside one event they go from top to bottom.
auto full = _items.size();
auto from = full - count;
for (auto i = 0, toReverse = count / 2; i != toReverse; ++i) {
std::swap(_items[from + i], _items[full - i - 1]);
}
}
}
auto newItemsCount = _items.size();
if (newItemsCount != oldItemsCount) {
for (auto i = oldItemsCount; i != newItemsCount + 1; ++i) {
if (i > 0) {
auto item = _items[i - 1].get();
if (i == newItemsCount) {
item->setLogEntryDisplayDate(true);
} else {
auto previous = _items[i].get();
item->setLogEntryDisplayDate(item->date.date() != previous->date.date());
auto attachToPrevious = item->computeIsAttachToPrevious(previous);
item->setLogEntryAttachToPrevious(attachToPrevious);
previous->setLogEntryAttachToNext(attachToPrevious);
}
}
}
updateMinMaxIds();
itemsAdded(direction);
}
} else {
loadedFlag = true;
}
update();
}).fail([this, &requestId, &loadedFlag](const RPCError &error) {
requestId = 0;
loadedFlag = true;
@ -493,6 +489,61 @@ void InnerWidget::preloadMore(Direction direction) {
}).send();
}
void InnerWidget::addEvents(Direction direction, const QVector<MTPChannelAdminLogEvent> &events) {
if (_filterChanged) {
clearAfterFilterChange();
}
auto up = (direction == Direction::Up);
if (events.empty()) {
(up ? _upLoaded : _downLoaded) = true;
update();
return;
}
// When loading items up we just add them to the back of the _items vector.
// When loading items down we add them to a new vector and copy _items after them.
auto newItemsForDownDirection = std::vector<HistoryItemOwned>();
auto oldItemsCount = _items.size();
auto &addToItems = (direction == Direction::Up) ? _items : newItemsForDownDirection;
addToItems.reserve(oldItemsCount + events.size() * 2);
for_const (auto &event, events) {
t_assert(event.type() == mtpc_channelAdminLogEvent);
auto &data = event.c_channelAdminLogEvent();
if (_itemsByIds.find(data.vid.v) != _itemsByIds.cend()) {
continue;
}
auto count = 0;
GenerateItems(_history, _idManager, data, [this, id = data.vid.v, &addToItems, &count](HistoryItemOwned item) {
_itemsByIds.emplace(id, item.get());
addToItems.push_back(std::move(item));
++count;
});
if (count > 1) {
// Reverse the inner order of the added messages, because we load events
// from bottom to top but inside one event they go from top to bottom.
auto full = addToItems.size();
auto from = full - count;
for (auto i = 0, toReverse = count / 2; i != toReverse; ++i) {
std::swap(addToItems[from + i], addToItems[full - i - 1]);
}
}
}
auto newItemsCount = _items.size() + ((direction == Direction::Up) ? 0 : newItemsForDownDirection.size());
if (newItemsCount != oldItemsCount) {
if (direction == Direction::Down) {
for (auto &item : _items) {
newItemsForDownDirection.push_back(std::move(item));
}
_items = std::move(newItemsForDownDirection);
}
updateMinMaxIds();
itemsAdded(direction, newItemsCount - oldItemsCount);
}
update();
}
void InnerWidget::updateMinMaxIds() {
if (_itemsByIds.empty() || _filterChanged) {
_maxId = _minId = 0;
@ -505,7 +556,24 @@ void InnerWidget::updateMinMaxIds() {
}
}
void InnerWidget::itemsAdded(Direction direction) {
void InnerWidget::itemsAdded(Direction direction, int addedCount) {
Expects(addedCount >= 0);
auto checkFrom = (direction == Direction::Up) ? (_items.size() - addedCount) : 1; // Should be ": 0", but zero is skipped anyway.
auto checkTo = (direction == Direction::Up) ? (_items.size() + 1) : (addedCount + 1);
for (auto i = checkFrom; i != checkTo; ++i) {
if (i > 0) {
auto item = _items[i - 1].get();
if (i < _items.size()) {
auto previous = _items[i].get();
item->setLogEntryDisplayDate(item->date.date() != previous->date.date());
auto attachToPrevious = item->computeIsAttachToPrevious(previous);
item->setLogEntryAttachToPrevious(attachToPrevious);
previous->setLogEntryAttachToNext(attachToPrevious);
} else {
item->setLogEntryDisplayDate(true);
}
}
}
updateSize();
}
@ -731,9 +799,10 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
auto item = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem();
auto lnkPhoto = dynamic_cast<PhotoClickHandler*>(_contextMenuLink.data());
auto lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLink.data());
bool lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideo() : false;
bool lnkIsAudio = lnkDocument ? (lnkDocument->document()->voice() != nullptr) : false;
bool lnkIsSong = lnkDocument ? (lnkDocument->document()->song() != nullptr) : false;
auto lnkPeer = dynamic_cast<PeerClickHandler*>(_contextMenuLink.data());
auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideo() : false;
auto lnkIsAudio = lnkDocument ? (lnkDocument->document()->voice() != nullptr) : false;
auto lnkIsSong = lnkDocument ? (lnkDocument->document()->song() != nullptr) : false;
if (lnkPhoto || lnkDocument) {
if (isUponSelected > 0) {
_menu->addAction(lang(lng_context_copy_selected), [this] { copySelectedText(); })->setEnabled(true);
@ -766,6 +835,10 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (App::hoveredLinkItem()) {
App::contextItem(App::hoveredLinkItem());
}
} else if (lnkPeer) { // suggest to block
if (auto user = lnkPeer->peer()->asUser()) {
suggestRestrictUser(user);
}
} else { // maybe cursor on some text history item?
bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg());
bool canForward = item && item->canForward();
@ -934,6 +1007,71 @@ void InnerWidget::setToClipboard(const TextWithEntities &forClipboard, QClipboar
}
}
void InnerWidget::suggestRestrictUser(gsl::not_null<UserData*> user) {
Expects(_menu != nullptr);
if (!_channel->isMegagroup() || !_channel->canBanMembers() || _admins.empty()) {
return;
}
if (base::contains(_admins, user)) {
if (!base::contains(_adminsCanEdit, user)) {
return;
}
}
_menu->addAction(lang(lng_context_restrict_user), [this, user] {
auto editRestrictions = [user, this](bool hasAdminRights, const MTPChannelBannedRights &currentRights) {
auto weak = QPointer<InnerWidget>(this);
auto box = std::make_shared<QPointer<EditRestrictedBox>>();
*box = Ui::show(Box<EditRestrictedBox>(_channel, user, hasAdminRights, currentRights, [user, weak, box](const MTPChannelBannedRights &rights) {
if (weak) {
weak->restrictUser(user, rights);
}
if (*box) {
(*box)->closeBox();
}
}), KeepOtherLayers);
};
if (base::contains(_admins, user)) {
editRestrictions(true, EditRestrictedBox::DefaultRights(_channel));
} else {
request(MTPchannels_GetParticipant(_channel->inputChannel, user->inputUser)).done([this, editRestrictions](const MTPchannels_ChannelParticipant &result) {
Expects(result.type() == mtpc_channels_channelParticipant);
auto &participant = result.c_channels_channelParticipant();
App::feedUsers(participant.vusers);
auto type = participant.vparticipant.type();
if (type == mtpc_channelParticipantBanned) {
editRestrictions(false, participant.vparticipant.c_channelParticipantBanned().vbanned_rights);
} else {
auto hasAdminRights = (type == mtpc_channelParticipantAdmin || type == mtpc_channelParticipantCreator);
editRestrictions(hasAdminRights, EditRestrictedBox::DefaultRights(_channel));
}
}).fail([this, editRestrictions](const RPCError &error) {
editRestrictions(false, EditRestrictedBox::DefaultRights(_channel));
}).send();
}
});
}
void InnerWidget::restrictUser(gsl::not_null<UserData*> user, const MTPChannelBannedRights &rights) {
auto weak = QPointer<InnerWidget>(this);
MTP::send(MTPchannels_EditBanned(_channel->inputChannel, user->inputUser, rights), rpcDone([megagroup = _channel.get(), user, weak, rights](const MTPUpdates &result) {
AuthSession::Current().api().applyUpdates(result);
megagroup->applyEditBanned(user, rights);
if (weak) {
weak->restrictUserDone(user, rights);
}
}));
}
void InnerWidget::restrictUserDone(gsl::not_null<UserData*> user, const MTPChannelBannedRights &rights) {
Expects(rights.type() == mtpc_channelBannedRights);
if (rights.c_channelBannedRights().vflags.v) {
_admins.erase(std::remove(_admins.begin(), _admins.end(), user), _admins.end());
_adminsCanEdit.erase(std::remove(_adminsCanEdit.begin(), _adminsCanEdit.end(), user), _adminsCanEdit.end());
}
_downLoaded = false;
checkPreloadMore();
}
void InnerWidget::mousePressEvent(QMouseEvent *e) {
if (_menu) {
e->accept();

View File

@ -66,10 +66,8 @@ public:
// Empty "flags" means all events.
void applyFilter(FilterValue &&value);
FilterValue filter() const {
return _filter;
}
void applySearch(const QString &query);
void showFilter(base::lambda<void(FilterValue &&filter)> callback);
// AbstractTooltipShower interface
QString tooltipText() const override;
@ -131,17 +129,22 @@ private:
void copySelectedText();
TextWithEntities getSelectedText() const;
void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard);
void suggestRestrictUser(gsl::not_null<UserData*> user);
void restrictUser(gsl::not_null<UserData*> user, const MTPChannelBannedRights &rights);
void restrictUserDone(gsl::not_null<UserData*> user, const MTPChannelBannedRights &rights);
void requestAdmins();
void checkPreloadMore();
void updateVisibleTopItem();
void preloadMore(Direction direction);
void itemsAdded(Direction direction);
void itemsAdded(Direction direction, int addedCount);
void updateSize();
void updateMinMaxIds();
void updateEmptyText();
void paintEmpty(Painter &p);
void clearAfterFilterChange();
void clearAndRequestLog();
void addEvents(Direction direction, const QVector<MTPChannelAdminLogEvent> &events);
void toggleScrollDateShown();
void repaintScrollDateCallback();
@ -232,6 +235,9 @@ private:
FilterValue _filter;
QString _searchQuery;
std::vector<gsl::not_null<UserData*>> _admins;
std::vector<gsl::not_null<UserData*>> _adminsCanEdit;
base::lambda<void(FilterValue &&filter)> _showFilterCallback;
};

View File

@ -250,7 +250,7 @@ void GenerateItems(gsl::not_null<History*> history, LocalIdManager &idManager, c
using Flag = MTPDmessage::Flag;
auto fromName = App::peerName(from);
auto fromLink = peerOpenClickHandler(from);
auto fromLink = from->createOpenLink();
auto fromLinkText = textcmdLink(1, fromName);
auto addSimpleServiceMessage = [&](const QString &text, PhotoData *photo = nullptr) {

View File

@ -39,9 +39,6 @@ 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, gsl::not_null<ChannelData*> channel);
@ -264,37 +261,10 @@ Widget::Widget(QWidget *parent, gsl::not_null<Window::Controller*> controller, g
}
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();
}));
}
_inner->showFilter([this](FilterValue &&filter) {
applyFilter(std::move(filter));
Ui::hideLayer();
});
}
void Widget::updateAdaptiveLayout() {
@ -350,13 +320,11 @@ 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());

View File

@ -80,7 +80,7 @@ private:
};
class Widget final : public Window::SectionWidget, private MTP::Sender {
class Widget final : public Window::SectionWidget {
public:
Widget(QWidget *parent, gsl::not_null<Window::Controller*> controller, gsl::not_null<ChannelData*> channel);
@ -128,7 +128,6 @@ private:
object_ptr<FixedBar> _fixedBar;
object_ptr<Ui::PlainShadow> _fixedBarShadow;
object_ptr<Ui::FlatButton> _whatIsThis;
std::vector<gsl::not_null<UserData*>> _admins;
};
@ -152,9 +151,15 @@ public:
void setAdmins(std::vector<gsl::not_null<UserData*>> admins) {
_admins = std::move(admins);
}
void setAdminsCanEdit(std::vector<gsl::not_null<UserData*>> admins) {
_adminsCanEdit = std::move(admins);
}
std::vector<gsl::not_null<UserData*>> takeAdmins() {
return std::move(_admins);
}
std::vector<gsl::not_null<UserData*>> takeAdminsCanEdit() {
return std::move(_adminsCanEdit);
}
void setItems(std::vector<HistoryItemOwned> &&items, std::map<uint64, HistoryItem*> &&itemsByIds, bool upLoaded, bool downLoaded) {
_items = std::move(items);
@ -197,6 +202,7 @@ private:
gsl::not_null<ChannelData*> _channel;
int _scrollTop = 0;
std::vector<gsl::not_null<UserData*>> _admins;
std::vector<gsl::not_null<UserData*>> _adminsCanEdit;
std::vector<HistoryItemOwned> _items;
std::map<uint64, HistoryItem*> _itemsByIds;
bool _upLoaded = false;

View File

@ -53,7 +53,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
result.text = lng_action_user_joined(lt_from, fromLinkText());
} else {
result.links.push_back(fromLink());
result.links.push_back(peerOpenClickHandler(u));
result.links.push_back(u->createOpenLink());
result.text = lng_action_add_user(lt_from, fromLinkText(), lt_user, textcmdLink(2, u->name));
}
} else if (users.isEmpty()) {
@ -63,7 +63,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
result.links.push_back(fromLink());
for (auto i = 0, l = users.size(); i != l; ++i) {
auto user = App::user(peerFromUser(users[i]));
result.links.push_back(peerOpenClickHandler(user));
result.links.push_back(user->createOpenLink());
auto linkText = textcmdLink(i + 2, user->name);
if (i == 0) {
@ -123,7 +123,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
} else {
auto user = App::user(peerFromUser(action.vuser_id));
result.links.push_back(fromLink());
result.links.push_back(peerOpenClickHandler(user));
result.links.push_back(user->createOpenLink());
result.text = lng_action_kick_user(lt_from, fromLinkText(), lt_user, textcmdLink(2, user->name));
}
return result;
@ -687,7 +687,7 @@ HistoryJoined::PreparedText HistoryJoined::GenerateText(gsl::not_null<History*>
return { lang(history->isMegagroup() ? lng_action_you_joined_group : lng_action_you_joined) };
}
auto result = PreparedText {};
result.links.push_back(peerOpenClickHandler(inviter));
result.links.push_back(inviter->createOpenLink());
result.text = (history->isMegagroup() ? lng_action_add_you_group : lng_action_add_you)(lt_from, textcmdLink(1, inviter->name));
return result;
}

View File

@ -115,7 +115,7 @@ protected:
return textcmdLink(1, _from->name);
};
ClickHandlerPtr fromLink() const {
return peerOpenClickHandler(_from);
return _from->createOpenLink();
};
void removeMedia();

View File

@ -248,6 +248,23 @@ using UpdateFlag = Notify::PeerUpdate::Flag;
NotifySettings globalNotifyAll, globalNotifyUsers, globalNotifyChats;
NotifySettingsPtr globalNotifyAllPtr = UnknownNotifySettings, globalNotifyUsersPtr = UnknownNotifySettings, globalNotifyChatsPtr = UnknownNotifySettings;
PeerClickHandler::PeerClickHandler(gsl::not_null<PeerData*> peer) : _peer(peer) {
}
void PeerClickHandler::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton && App::main()) {
if (_peer && _peer->isChannel() && App::main()->historyPeer() != _peer) {
if (!_peer->asChannel()->isPublic() && !_peer->asChannel()->amIn()) {
Ui::show(Box<InformBox>(lang((_peer->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible)));
} else {
Ui::showPeerHistory(_peer, ShowAtUnreadMsgId, Ui::ShowWay::Forward);
}
} else {
Ui::showPeerProfile(_peer);
}
}
}
PeerData::PeerData(const PeerId &id) : id(id), _colorIndex(peerColorIndex(id)) {
nameText.setText(st::msgNameStyle, QString(), _textNameOptions);
_userpicEmpty.set(_colorIndex, QString());
@ -304,6 +321,10 @@ void PeerData::updateNameDelayed(const QString &newName, const QString &newNameO
Notify::peerUpdatedDelayed(update);
}
ClickHandlerPtr PeerData::createOpenLink() {
return MakeShared<PeerClickHandler>(this);
}
void PeerData::setUserpic(ImagePtr userpic) {
_userpic = userpic;
if (!_userpic || !_userpic->loaded()) {
@ -2106,22 +2127,6 @@ GameData::GameData(const GameId &id, const uint64 &accessHash, const QString &sh
, document(document) {
}
ClickHandlerPtr peerOpenClickHandler(PeerData *peer) {
return MakeShared<LambdaClickHandler>([peer] {
if (App::main()) {
if (peer && peer->isChannel() && App::main()->historyPeer() != peer) {
if (!peer->asChannel()->isPublic() && !peer->asChannel()->amIn()) {
Ui::show(Box<InformBox>(lang((peer->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible)));
} else {
Ui::showPeerHistory(peer, ShowAtUnreadMsgId, Ui::ShowWay::Forward);
}
} else {
Ui::showPeerProfile(peer);
}
}
});
}
MsgId clientMsgId() {
static MsgId currentClientMsgId = StartClientMsgId;
t_assert(currentClientMsgId < EndClientMsgId);

View File

@ -267,7 +267,21 @@ inline const QString &emptyUsername() {
return empty;
}
ClickHandlerPtr peerOpenClickHandler(PeerData *peer);
class PeerData;
class PeerClickHandler : public ClickHandler {
public:
PeerClickHandler(gsl::not_null<PeerData*> peer);
void onClick(Qt::MouseButton button) const override;
gsl::not_null<PeerData*> peer() const {
return _peer;
}
private:
gsl::not_null<PeerData*> _peer;
};
class UserData;
class ChatData;
@ -375,9 +389,10 @@ public:
return QString();
}
ClickHandlerPtr createOpenLink();
const ClickHandlerPtr &openLink() {
if (!_openLink) {
_openLink = peerOpenClickHandler(this);
_openLink = createOpenLink();
}
return _openLink;
}