2016-05-31 19:27:11 +00:00
|
|
|
/*
|
|
|
|
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-2016 John Preston, https://desktop.telegram.org
|
|
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "profile/profile_members_widget.h"
|
|
|
|
|
|
|
|
#include "styles/style_profile.h"
|
2016-06-03 07:20:24 +00:00
|
|
|
#include "mtproto/file_download.h"
|
2016-06-02 13:02:55 +00:00
|
|
|
#include "ui/buttons/left_outline_button.h"
|
2016-06-03 07:20:24 +00:00
|
|
|
#include "ui/flatlabel.h"
|
2016-06-02 13:02:55 +00:00
|
|
|
#include "boxes/contactsbox.h"
|
2016-06-03 07:20:24 +00:00
|
|
|
#include "boxes/confirmbox.h"
|
|
|
|
#include "apiwrap.h"
|
2016-06-02 13:02:55 +00:00
|
|
|
#include "observer_peer.h"
|
2016-05-31 19:27:11 +00:00
|
|
|
#include "lang.h"
|
|
|
|
|
|
|
|
namespace Profile {
|
|
|
|
|
2016-06-02 13:02:55 +00:00
|
|
|
using UpdateFlag = Notify::PeerUpdate::Flag;
|
|
|
|
|
2016-06-03 07:20:24 +00:00
|
|
|
MembersWidget::MembersWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_participants_section)) {
|
|
|
|
setMouseTracking(true);
|
|
|
|
|
|
|
|
_removeWidth = st::normalFont->width(lang(lng_profile_kick));
|
|
|
|
|
|
|
|
_updateOnlineTimer.setSingleShot(true);
|
|
|
|
connect(&_updateOnlineTimer, SIGNAL(timeout()), this, SLOT(onUpdateOnlineDisplay()));
|
|
|
|
|
|
|
|
auto observeEvents = UpdateFlag::AdminsChanged | UpdateFlag::MembersChanged | UpdateFlag::UserOnlineChanged;
|
|
|
|
Notify::registerPeerObserver(observeEvents, this, &MembersWidget::notifyPeerUpdated);
|
|
|
|
FileDownload::registerImageLoadedObserver(this, &MembersWidget::repaintCallback);
|
|
|
|
|
|
|
|
refreshMembers();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::repaintCallback() {
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
|
|
|
if (update.peer != peer()) {
|
|
|
|
if (update.flags & UpdateFlag::UserOnlineChanged) {
|
|
|
|
if (auto user = update.peer->asUser()) {
|
|
|
|
refreshUserOnline(user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (update.flags & UpdateFlag::MembersChanged) {
|
|
|
|
refreshMembers();
|
|
|
|
contentSizeUpdated();
|
|
|
|
} else if (update.flags & UpdateFlag::AdminsChanged) {
|
|
|
|
if (auto chat = peer()->asChat()) {
|
|
|
|
for_const (auto member, _list) {
|
|
|
|
setMemberFlags(member, chat);
|
|
|
|
}
|
|
|
|
} else if (auto megagroup = peer()->asMegagroup()) {
|
|
|
|
for_const (auto member, _list) {
|
|
|
|
setMemberFlags(member, megagroup);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
repaintCallback();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::refreshUserOnline(UserData *user) {
|
|
|
|
auto it = _membersByUser.find(user);
|
|
|
|
if (it == _membersByUser.cend()) return;
|
|
|
|
|
|
|
|
_now = unixtime();
|
|
|
|
|
|
|
|
auto member = it.value();
|
|
|
|
member->online = !user->botInfo && App::onlineColorUse(user->onlineTill, _now);
|
|
|
|
member->onlineTill = user->onlineTill;
|
|
|
|
member->onlineForSort = user->isSelf() ? INT_MAX : App::onlineForSort(user, _now);
|
|
|
|
member->onlineText = QString();
|
|
|
|
|
|
|
|
sortMembers();
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) {
|
|
|
|
_visibleTop = visibleTop;
|
|
|
|
_visibleBottom = visibleBottom;
|
|
|
|
|
|
|
|
if (auto megagroup = peer()->asMegagroup()) {
|
|
|
|
auto megagroupInfo = megagroup->mgInfo;
|
|
|
|
if (!megagroupInfo->lastParticipants.isEmpty() && megagroupInfo->lastParticipants.size() < megagroup->membersCount()) {
|
|
|
|
if (_visibleTop + PreloadHeightsCount * (_visibleBottom - _visibleTop) > height()) {
|
|
|
|
App::api()->requestLastParticipants(megagroup, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
preloadUserPhotos();
|
|
|
|
updateSelection();
|
2016-05-31 19:27:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int MembersWidget::resizeGetHeight(int newWidth) {
|
|
|
|
int newHeight = contentTop();
|
|
|
|
|
2016-06-03 07:20:24 +00:00
|
|
|
if (_limitReachedInfo) {
|
|
|
|
int limitReachedInfoWidth = newWidth - getListLeft();
|
|
|
|
_limitReachedInfo->resizeToWidth(limitReachedInfoWidth);
|
|
|
|
newHeight = getListTop();
|
|
|
|
}
|
|
|
|
|
|
|
|
newHeight += _list.size() * st::profileMemberHeight;
|
|
|
|
|
2016-05-31 19:27:11 +00:00
|
|
|
return newHeight;
|
|
|
|
}
|
|
|
|
|
2016-06-03 07:20:24 +00:00
|
|
|
void MembersWidget::paintContents(Painter &p) {
|
|
|
|
int left = getListLeft();
|
|
|
|
int top = getListTop();
|
|
|
|
if (_limitReachedInfo) {
|
|
|
|
int infoTop = contentTop();
|
|
|
|
int infoHeight = top - infoTop - st::profileLimitReachedSkip;
|
|
|
|
paintOutlinedRect(p, left, infoTop, width() - left, infoHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
_now = unixtime();
|
|
|
|
int from = floorclamp(_visibleTop - top, st::profileMemberHeight, 0, _list.size());
|
|
|
|
int to = ceilclamp(_visibleBottom - top, st::profileMemberHeight, 0, _list.size());
|
|
|
|
for (int i = from; i < to; ++i) {
|
|
|
|
int y = top + i * st::profileMemberHeight;
|
|
|
|
bool selected = (i == _selected);
|
|
|
|
bool selectedKick = selected && _selectedKick;
|
|
|
|
if (_pressed >= 0) {
|
|
|
|
if (_pressed != _selected) {
|
|
|
|
selected = selectedKick = false;
|
|
|
|
} else if (!_pressedKick) {
|
|
|
|
_selectedKick = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
paintMember(p, left, y, _list.at(i), selected, selectedKick);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::paintOutlinedRect(Painter &p, int x, int y, int w, int h) const {
|
|
|
|
int outlineWidth = st::defaultLeftOutlineButton.outlineWidth;
|
|
|
|
p.fillRect(rtlrect(x, y, outlineWidth, h, width()), st::defaultLeftOutlineButton.outlineFgOver);
|
|
|
|
p.fillRect(rtlrect(x + outlineWidth, y, w - outlineWidth, h, width()), st::defaultLeftOutlineButton.textBgOver);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::mouseMoveEvent(QMouseEvent *e) {
|
|
|
|
_mousePosition = e->globalPos();
|
|
|
|
updateSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::mousePressEvent(QMouseEvent *e) {
|
|
|
|
_mousePosition = e->globalPos();
|
|
|
|
updateSelection();
|
|
|
|
|
|
|
|
_pressed = _selected;
|
|
|
|
_pressedKick = _selectedKick;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::mouseReleaseEvent(QMouseEvent *e) {
|
|
|
|
_mousePosition = e->globalPos();
|
|
|
|
updateSelection();
|
|
|
|
|
|
|
|
auto pressed = _pressed;
|
|
|
|
auto pressedKick = _pressedKick;
|
|
|
|
_pressed = -1;
|
|
|
|
_pressedKick = false;
|
|
|
|
if (pressed >= 0 && pressed < _list.size() && pressed == _selected && pressedKick == _selectedKick) {
|
|
|
|
auto member = _list.at(pressed);
|
|
|
|
if (pressedKick) {
|
|
|
|
_kicking = member->user;
|
|
|
|
ConfirmBox *box = new ConfirmBox(lng_profile_sure_kick(lt_user, _kicking->firstName), lang(lng_box_remove));
|
|
|
|
connect(box, SIGNAL(confirmed()), this, SLOT(onKickConfirm()));
|
|
|
|
Ui::showLayer(box);
|
|
|
|
} else {
|
|
|
|
Ui::showPeerProfile(member->user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setCursor(_selectedKick ? style::cur_pointer : style::cur_default);
|
|
|
|
repaintSelectedRow();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::enterEvent(QEvent *e) {
|
|
|
|
_mousePosition = QCursor::pos();
|
|
|
|
updateSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::leaveEvent(QEvent *e) {
|
|
|
|
_mousePosition = QPoint(-1, -1);
|
|
|
|
updateSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::updateSelection() {
|
|
|
|
int selected = -1;
|
|
|
|
bool selectedKick = false;
|
|
|
|
|
|
|
|
auto mouse = mapFromGlobal(_mousePosition);
|
|
|
|
if (rtl()) mouse.setX(width() - mouse.x());
|
|
|
|
int left = getListLeft();
|
|
|
|
int top = getListTop();
|
|
|
|
if (mouse.x() >= left && mouse.x() < width() && mouse.y() >= top) {
|
|
|
|
selected = (mouse.y() - top) / st::profileMemberHeight;
|
|
|
|
if (selected >= _list.size()) {
|
|
|
|
selected = -1;
|
|
|
|
} else if (_list.at(selected)->canBeKicked) {
|
|
|
|
int skip = st::profileMemberPhotoPosition.x();
|
|
|
|
int nameLeft = left + st::profileMemberNamePosition.x();
|
|
|
|
int nameTop = top + _selected * st::profileMemberHeight + st::profileMemberNamePosition.y();
|
|
|
|
int nameWidth = width() - nameLeft - skip;
|
|
|
|
if (mouse.x() >= nameLeft + nameWidth - _removeWidth && mouse.x() < nameLeft + nameWidth) {
|
|
|
|
if (mouse.y() >= nameTop && mouse.y() < nameTop + st::normalFont->height) {
|
|
|
|
selectedKick = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setSelected(selected, selectedKick);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::setSelected(int selected, bool selectedKick) {
|
|
|
|
if (_selected == selected && _selectedKick == selectedKick) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
repaintSelectedRow();
|
|
|
|
if (_selectedKick != selectedKick) {
|
|
|
|
_selectedKick = selectedKick;
|
|
|
|
if (_pressed < 0) {
|
|
|
|
setCursor(_selectedKick ? style::cur_pointer : style::cur_default);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_selected != selected) {
|
|
|
|
_selected = selected;
|
|
|
|
repaintSelectedRow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::repaintSelectedRow() {
|
|
|
|
if (_selected >= 0) {
|
|
|
|
int left = getListLeft();
|
|
|
|
rtlupdate(left, getListTop() + _selected * st::profileMemberHeight, width() - left, st::profileMemberHeight);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int MembersWidget::getListLeft() const {
|
|
|
|
return st::profileBlockTitlePosition.x() - st::profileMemberPaddingLeft;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MembersWidget::getListTop() const {
|
|
|
|
int result = contentTop();
|
|
|
|
if (_limitReachedInfo) {
|
|
|
|
result += st::defaultLeftOutlineButton.padding.top();
|
|
|
|
result += _limitReachedInfo->height();
|
|
|
|
result += st::defaultLeftOutlineButton.padding.bottom();
|
|
|
|
result += st::profileLimitReachedSkip;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::refreshMembers() {
|
|
|
|
_now = unixtime();
|
|
|
|
if (auto chat = peer()->asChat()) {
|
|
|
|
checkSelfAdmin(chat);
|
|
|
|
if (chat->noParticipantInfo()) {
|
|
|
|
App::api()->requestFullPeer(chat);
|
|
|
|
}
|
|
|
|
fillChatMembers(chat);
|
|
|
|
} else if (auto megagroup = peer()->asMegagroup()) {
|
|
|
|
checkSelfAdmin(megagroup);
|
|
|
|
auto megagroupInfo = megagroup->mgInfo;
|
|
|
|
if (megagroupInfo->lastParticipants.isEmpty() || megagroup->lastParticipantsCountOutdated()) {
|
|
|
|
App::api()->requestLastParticipants(megagroup);
|
|
|
|
}
|
|
|
|
fillMegagroupMembers(megagroup);
|
|
|
|
}
|
|
|
|
sortMembers();
|
|
|
|
|
|
|
|
refreshVisibility();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::checkSelfAdmin(ChatData *chat) {
|
|
|
|
if (chat->participants.isEmpty()) return;
|
|
|
|
|
|
|
|
auto self = App::self();
|
|
|
|
if (chat->amAdmin() && !chat->admins.contains(self)) {
|
|
|
|
chat->admins.insert(self);
|
|
|
|
} else if (!chat->amAdmin() && chat->admins.contains(self)) {
|
|
|
|
chat->admins.remove(self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::checkSelfAdmin(ChannelData *megagroup) {
|
|
|
|
if (megagroup->mgInfo->lastParticipants.isEmpty()) return;
|
|
|
|
|
|
|
|
bool amAdmin = (megagroup->amCreator() || megagroup->amEditor());
|
|
|
|
auto self = App::self();
|
|
|
|
if (amAdmin && !megagroup->mgInfo->lastAdmins.contains(self)) {
|
|
|
|
megagroup->mgInfo->lastAdmins.insert(self);
|
|
|
|
} else if (!amAdmin && megagroup->mgInfo->lastAdmins.contains(self)) {
|
|
|
|
megagroup->mgInfo->lastAdmins.remove(self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::preloadUserPhotos() {
|
|
|
|
int top = getListTop();
|
|
|
|
int preloadFor = (_visibleBottom - _visibleTop) * PreloadHeightsCount;
|
|
|
|
int from = floorclamp(_visibleTop - top, st::profileMemberHeight, 0, _list.size());
|
|
|
|
int to = ceilclamp(_visibleBottom + preloadFor - top, st::profileMemberHeight, 0, _list.size());
|
|
|
|
for (int i = from; i < to; ++i) {
|
|
|
|
_list.at(i)->user->loadUserpic();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::refreshVisibility() {
|
|
|
|
setVisible(!_list.isEmpty());
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::sortMembers() {
|
|
|
|
if (!_sortByOnline || _list.isEmpty()) return;
|
|
|
|
|
|
|
|
qSort(_list.begin(), _list.end(), [](Member *a, Member *b) -> bool {
|
|
|
|
return a->onlineForSort > b->onlineForSort;
|
|
|
|
});
|
|
|
|
|
|
|
|
updateOnlineCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::updateOnlineCount() {
|
|
|
|
bool onlyMe = true;
|
|
|
|
int newOnlineCount = 0;
|
|
|
|
for_const (auto member, _list) {
|
|
|
|
bool isOnline = !member->user->botInfo && App::onlineColorUse(member->onlineTill, _now);
|
|
|
|
if (member->online != isOnline) {
|
|
|
|
member->online = isOnline;
|
|
|
|
member->onlineText = QString();
|
|
|
|
}
|
|
|
|
if (member->online) {
|
|
|
|
++newOnlineCount;
|
|
|
|
if (!member->user->isSelf()) {
|
|
|
|
onlyMe = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (newOnlineCount == 1 && onlyMe) {
|
|
|
|
newOnlineCount = 0;
|
|
|
|
}
|
|
|
|
if (_onlineCount != newOnlineCount) {
|
|
|
|
_onlineCount = newOnlineCount;
|
|
|
|
emit onlineCountUpdated(_onlineCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MembersWidget::Member *MembersWidget::addUser(ChatData *chat, UserData *user) {
|
|
|
|
auto member = getMember(user);
|
|
|
|
setMemberFlags(member, chat);
|
|
|
|
_list.push_back(member);
|
|
|
|
return member;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::fillChatMembers(ChatData *chat) {
|
|
|
|
if (chat->participants.isEmpty()) return;
|
|
|
|
|
|
|
|
_list.clear();
|
|
|
|
if (!chat->amIn()) return;
|
|
|
|
|
|
|
|
_sortByOnline = true;
|
|
|
|
|
|
|
|
_list.reserve(chat->participants.size());
|
|
|
|
addUser(chat, App::self())->onlineForSort = INT_MAX; // Put me on the first place.
|
|
|
|
for (auto i = chat->participants.cbegin(), e = chat->participants.cend(); i != e; ++i) {
|
|
|
|
auto user = i.key();
|
|
|
|
if (!user->isSelf()) {
|
|
|
|
addUser(chat, user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::setMemberFlags(Member *member, ChatData *chat) {
|
|
|
|
if (member->user->id == peerFromUser(MTP::authedId())) {
|
|
|
|
member->canBeKicked = false;
|
|
|
|
} else if (chat->amCreator() || (chat->amAdmin() && !member->isAdmin)) {
|
|
|
|
member->canBeKicked = true;
|
|
|
|
} else {
|
|
|
|
member->canBeKicked = chat->invitedByMe.contains(member->user);
|
|
|
|
}
|
|
|
|
member->isAdmin = chat->admins.contains(member->user);
|
|
|
|
}
|
|
|
|
|
|
|
|
MembersWidget::Member *MembersWidget::addUser(ChannelData *megagroup, UserData *user) {
|
|
|
|
auto member = getMember(user);
|
|
|
|
setMemberFlags(member, megagroup);
|
|
|
|
_list.push_back(member);
|
|
|
|
return member;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::fillMegagroupMembers(ChannelData *megagroup) {
|
|
|
|
t_assert(megagroup->mgInfo != nullptr);
|
|
|
|
if (megagroup->mgInfo->lastParticipants.isEmpty()) return;
|
|
|
|
|
|
|
|
if (!megagroup->amIn()) {
|
|
|
|
_list.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_sortByOnline = (megagroup->membersCount() > 0 && megagroup->membersCount() <= Global::ChatSizeMax());
|
|
|
|
|
|
|
|
auto &membersList = megagroup->mgInfo->lastParticipants;
|
|
|
|
if (_sortByOnline) {
|
|
|
|
_list.clear();
|
|
|
|
_list.reserve(membersList.size());
|
|
|
|
addUser(megagroup, App::self())->onlineForSort = INT_MAX;
|
|
|
|
} else if (membersList.size() >= _list.size()) {
|
|
|
|
if (addUsersToEnd(megagroup)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!_sortByOnline) {
|
|
|
|
_list.clear();
|
|
|
|
_list.reserve(membersList.size());
|
|
|
|
}
|
|
|
|
for_const (auto user, membersList) {
|
|
|
|
if (!_sortByOnline || !user->isSelf()) {
|
|
|
|
addUser(megagroup, user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MembersWidget::addUsersToEnd(ChannelData *megagroup) {
|
|
|
|
auto &membersList = megagroup->mgInfo->lastParticipants;
|
|
|
|
for (int i = 0, count = _list.size(); i < count; ++i) {
|
|
|
|
if (_list.at(i)->user != membersList.at(i)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_list.reserve(membersList.size());
|
|
|
|
for (int i = _list.size(), count = membersList.size(); i < count; ++i) {
|
|
|
|
addUser(megagroup, membersList.at(i));
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::setMemberFlags(Member *member, ChannelData *megagroup) {
|
|
|
|
if (member->user->isSelf()) {
|
|
|
|
member->canBeKicked = false;
|
|
|
|
} else if (megagroup->amCreator() || (megagroup->amEditor() && !member->isAdmin)) {
|
|
|
|
member->canBeKicked = true;
|
|
|
|
} else {
|
|
|
|
member->canBeKicked = false;
|
|
|
|
}
|
|
|
|
member->isAdmin = megagroup->mgInfo->lastAdmins.contains(member->user);
|
|
|
|
}
|
|
|
|
|
|
|
|
MembersWidget::Member *MembersWidget::getMember(UserData *user) {
|
|
|
|
auto it = _membersByUser.constFind(user);
|
|
|
|
if (it == _membersByUser.cend()) {
|
|
|
|
auto member = new Member(user);
|
|
|
|
it = _membersByUser.insert(user, member);
|
|
|
|
member->online = !user->botInfo && App::onlineColorUse(user->onlineTill, _now);
|
|
|
|
member->onlineTill = user->onlineTill;
|
|
|
|
member->onlineForSort = App::onlineForSort(user, _now);
|
|
|
|
}
|
|
|
|
return it.value();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::paintMember(Painter &p, int x, int y, Member *member, bool selected, bool selectedKick) {
|
|
|
|
if (selected) {
|
|
|
|
paintOutlinedRect(p, x, y, width() - x, st::profileMemberHeight);
|
|
|
|
}
|
|
|
|
int skip = st::profileMemberPhotoPosition.x();
|
|
|
|
|
|
|
|
member->user->paintUserpicLeft(p, st::profileMemberPhotoSize, x + st::profileMemberPhotoPosition.x(), y + st::profileMemberPhotoPosition.y(), width());
|
|
|
|
|
|
|
|
if (member->name.isEmpty()) {
|
|
|
|
member->name.setText(st::semiboldFont, App::peerName(member->user), _textNameOptions);
|
|
|
|
}
|
|
|
|
int nameLeft = x + st::profileMemberNamePosition.x();
|
|
|
|
int nameTop = y + st::profileMemberNamePosition.y();
|
|
|
|
int nameWidth = width() - nameLeft - skip;
|
|
|
|
if (member->canBeKicked && selected) {
|
|
|
|
p.setFont(selectedKick ? st::normalFont->underline() : st::normalFont);
|
|
|
|
p.setPen(st::windowActiveTextFg);
|
|
|
|
p.drawTextLeft(nameLeft + nameWidth - _removeWidth, nameTop, width(), lang(lng_profile_kick), _removeWidth);
|
|
|
|
nameWidth -= _removeWidth + skip;
|
|
|
|
}
|
|
|
|
if (member->isAdmin) {
|
|
|
|
nameWidth -= st::profileMemberAdminIcon.width();
|
|
|
|
int iconLeft = nameLeft + qMin(nameWidth, member->name.maxWidth());
|
|
|
|
st::profileMemberAdminIcon.paint(p, QPoint(iconLeft, nameTop), width());
|
|
|
|
}
|
|
|
|
p.setPen(st::profileMemberNameFg);
|
|
|
|
member->name.drawLeftElided(p, nameLeft, nameTop, nameWidth, width());
|
|
|
|
|
|
|
|
if (member->onlineText.isEmpty() || (member->onlineTextTill <= _now)) {
|
|
|
|
if (member->user->botInfo) {
|
|
|
|
bool seesAllMessages = (member->user->botInfo->readsAllHistory || member->isAdmin);
|
|
|
|
member->onlineText = lang(seesAllMessages ? lng_status_bot_reads_all : lng_status_bot_not_reads_all);
|
|
|
|
member->onlineTextTill = _now + 86400;
|
|
|
|
} else {
|
|
|
|
member->online = App::onlineColorUse(member->onlineTill, _now);
|
|
|
|
member->onlineText = App::onlineText(member->onlineTill, _now);
|
|
|
|
member->onlineTextTill = _now + App::onlineWillChangeIn(member->onlineTill, _now);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_updateOnlineAt <= _now || _updateOnlineAt > member->onlineTextTill) {
|
|
|
|
_updateOnlineAt = member->onlineTextTill;
|
|
|
|
_updateOnlineTimer.start((_updateOnlineAt - _now + 1) * 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (member->online) {
|
|
|
|
p.setPen(st::profileMemberStatusFgActive);
|
|
|
|
} else {
|
|
|
|
p.setPen(selected ? st::profileMemberStatusFgOver : st::profileMemberStatusFg);
|
|
|
|
}
|
|
|
|
p.setFont(st::normalFont);
|
|
|
|
p.drawTextLeft(x + st::profileMemberStatusPosition.x(), y + st::profileMemberStatusPosition.y(), width(), member->onlineText);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::onKickConfirm() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersWidget::onUpdateOnlineDisplay() {
|
|
|
|
if (_sortByOnline) {
|
|
|
|
_now = unixtime();
|
|
|
|
|
|
|
|
bool changed = false;
|
|
|
|
for_const (auto member, _list) {
|
|
|
|
if (!member->online) {
|
|
|
|
if (!member->user->isSelf()) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bool isOnline = !member->user->botInfo && App::onlineColorUse(member->onlineTill, _now);
|
|
|
|
if (!isOnline) {
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (changed) {
|
|
|
|
updateOnlineCount();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
MembersWidget::~MembersWidget() {
|
|
|
|
auto members = createAndSwap(_membersByUser);
|
|
|
|
for_const (auto member, members) {
|
|
|
|
delete member;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-02 13:02:55 +00:00
|
|
|
ChannelMembersWidget::ChannelMembersWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_participants_section)) {
|
|
|
|
auto observeEvents = UpdateFlag::ChannelCanViewAdmins
|
|
|
|
| UpdateFlag::ChannelCanViewMembers
|
|
|
|
| UpdateFlag::AdminsChanged
|
|
|
|
| UpdateFlag::MembersChanged;
|
|
|
|
Notify::registerPeerObserver(observeEvents, this, &ChannelMembersWidget::notifyPeerUpdated);
|
|
|
|
|
|
|
|
refreshButtons();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChannelMembersWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
|
|
|
if (update.peer != peer()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (update.flags & (UpdateFlag::ChannelCanViewAdmins | UpdateFlag::AdminsChanged)) {
|
|
|
|
refreshAdmins();
|
|
|
|
}
|
|
|
|
if (update.flags & (UpdateFlag::ChannelCanViewMembers | UpdateFlag::MembersChanged)) {
|
|
|
|
refreshMembers();
|
|
|
|
}
|
|
|
|
refreshVisibility();
|
|
|
|
|
|
|
|
contentSizeUpdated();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChannelMembersWidget::addButton(const QString &text, ChildWidget<Ui::LeftOutlineButton> *button, const char *slot) {
|
|
|
|
if (text.isEmpty()) {
|
|
|
|
button->destroy();
|
|
|
|
} else if (*button) {
|
|
|
|
(*button)->setText(text);
|
|
|
|
} else {
|
|
|
|
(*button) = new Ui::LeftOutlineButton(this, text);
|
|
|
|
(*button)->show();
|
|
|
|
connect(*button, SIGNAL(clicked()), this, slot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChannelMembersWidget::refreshButtons() {
|
|
|
|
refreshAdmins();
|
|
|
|
refreshMembers();
|
|
|
|
|
|
|
|
refreshVisibility();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChannelMembersWidget::refreshAdmins() {
|
|
|
|
auto getAdminsText = [this]() -> QString {
|
|
|
|
if (auto channel = peer()->asChannel()) {
|
|
|
|
if (!channel->isMegagroup() && channel->canViewAdmins()) {
|
|
|
|
int adminsCount = qMax(channel->adminsCount(), 1);
|
|
|
|
return lng_channel_admins_link(lt_count, adminsCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return QString();
|
|
|
|
};
|
|
|
|
addButton(getAdminsText(), &_admins, SLOT(onAdmins()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChannelMembersWidget::refreshMembers() {
|
|
|
|
auto getMembersText = [this]() -> QString {
|
|
|
|
if (auto channel = peer()->asChannel()) {
|
|
|
|
if (!channel->isMegagroup() && channel->canViewMembers()) {
|
|
|
|
int membersCount = qMax(channel->membersCount(), 1);
|
|
|
|
return lng_channel_members_link(lt_count, membersCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return QString();
|
|
|
|
};
|
|
|
|
addButton(getMembersText(), &_members, SLOT(onMembers()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChannelMembersWidget::refreshVisibility() {
|
|
|
|
setVisible(_admins || _members);
|
2016-05-31 19:27:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int ChannelMembersWidget::resizeGetHeight(int newWidth) {
|
|
|
|
int newHeight = contentTop();
|
|
|
|
|
2016-06-02 13:02:55 +00:00
|
|
|
auto resizeButton = [this, &newHeight, newWidth](ChildWidget<Ui::LeftOutlineButton> &button) {
|
|
|
|
if (!button) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int left = defaultOutlineButtonLeft();
|
|
|
|
int availableWidth = newWidth - left - st::profileBlockMarginRight;
|
|
|
|
accumulate_min(availableWidth, st::profileBlockOneLineWidthMax);
|
|
|
|
button->resizeToWidth(availableWidth);
|
|
|
|
button->moveToLeft(left, newHeight);
|
|
|
|
newHeight += button->height();
|
|
|
|
};
|
|
|
|
|
|
|
|
resizeButton(_admins);
|
|
|
|
resizeButton(_members);
|
|
|
|
|
2016-05-31 19:27:11 +00:00
|
|
|
return newHeight;
|
|
|
|
}
|
|
|
|
|
2016-06-02 13:02:55 +00:00
|
|
|
void ChannelMembersWidget::onAdmins() {
|
|
|
|
if (auto channel = peer()->asChannel()) {
|
|
|
|
Ui::showLayer(new MembersBox(channel, MembersFilterAdmins));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChannelMembersWidget::onMembers() {
|
|
|
|
if (auto channel = peer()->asChannel()) {
|
|
|
|
Ui::showLayer(new MembersBox(channel, MembersFilterRecent));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-31 19:27:11 +00:00
|
|
|
} // namespace Profile
|