mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-29 19:03:03 +00:00
Replace ContactsBox with PeerListBox in two cases.
- View contacts list in PeerListBox. - Add participants when creating group / channel in PeerListBox.
This commit is contained in:
parent
17cef93ac0
commit
f7359093b4
@ -65,7 +65,7 @@ enum class RegExOption {
|
||||
DontAutomaticallyOptimize = QRegularExpression::DontAutomaticallyOptimizeOption,
|
||||
#endif // OS_MAC_OLD
|
||||
};
|
||||
Q_DECLARE_FLAGS(RegExOptions, RegExOption);
|
||||
using RegExOptions = QFlags<RegExOption>;
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(RegExOptions);
|
||||
|
||||
inline RegularExpressionMatch regex_match(const QString &string, const QString &subject, RegExOptions options = 0) {
|
||||
|
@ -25,9 +25,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "lang/lang_keys.h"
|
||||
#include "messenger.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "boxes/contacts_box.h"
|
||||
#include "base/flat_set.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/photo_crop_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@ -53,8 +54,203 @@ style::InputField CreateBioFieldStyle() {
|
||||
return result;
|
||||
}
|
||||
|
||||
base::flat_set<gsl::not_null<UserData*>> GetAlreadyInFromPeer(PeerData *peer) {
|
||||
if (!peer) {
|
||||
return {};
|
||||
}
|
||||
if (auto chat = peer->asChat()) {
|
||||
auto participants = chat->participants.keys();
|
||||
return { participants.cbegin(), participants.cend() };
|
||||
} else if (auto channel = peer->asChannel()) {
|
||||
if (channel->isMegagroup()) {
|
||||
auto &participants = channel->mgInfo->lastParticipants;
|
||||
return { participants.cbegin(), participants.cend() };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
class AddParticipantsBoxController : public ContactsBoxController {
|
||||
public:
|
||||
AddParticipantsBoxController(PeerData *peer);
|
||||
AddParticipantsBoxController(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn);
|
||||
|
||||
using ContactsBoxController::ContactsBoxController;
|
||||
|
||||
void rowClicked(gsl::not_null<PeerListRow*> row) override;
|
||||
void itemDeselectedHook(gsl::not_null<PeerData*> peer) override;
|
||||
|
||||
protected:
|
||||
void prepareViewHook() override;
|
||||
std::unique_ptr<PeerListRow> createRow(gsl::not_null<UserData*> user) override;
|
||||
|
||||
private:
|
||||
int alreadyInCount() const;
|
||||
bool isAlreadyIn(gsl::not_null<UserData*> user) const;
|
||||
int fullCount() const;
|
||||
void updateTitle();
|
||||
|
||||
PeerData *_peer = nullptr;
|
||||
base::flat_set<gsl::not_null<UserData*>> _alreadyIn;
|
||||
|
||||
};
|
||||
|
||||
AddParticipantsBoxController::AddParticipantsBoxController(PeerData *peer)
|
||||
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
|
||||
, _peer(peer)
|
||||
, _alreadyIn(GetAlreadyInFromPeer(peer)) {
|
||||
}
|
||||
|
||||
AddParticipantsBoxController::AddParticipantsBoxController(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn)
|
||||
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
|
||||
, _peer(channel)
|
||||
, _alreadyIn(std::move(alreadyIn)) {
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::rowClicked(gsl::not_null<PeerListRow*> row) {
|
||||
auto count = fullCount();
|
||||
auto limit = (_peer && _peer->isMegagroup()) ? Global::MegagroupSizeMax() : Global::ChatSizeMax();
|
||||
if (count < limit || row->checked()) {
|
||||
delegate()->peerListSetRowChecked(row, !row->checked());
|
||||
updateTitle();
|
||||
} else if (auto channel = _peer ? _peer->asChannel() : nullptr) {
|
||||
if (!_peer->isMegagroup()) {
|
||||
Ui::show(Box<MaxInviteBox>(_peer->asChannel()), KeepOtherLayers);
|
||||
}
|
||||
} else if (count >= Global::ChatSizeMax() && count < Global::MegagroupSizeMax()) {
|
||||
Ui::show(Box<InformBox>(lng_profile_add_more_after_upgrade(lt_count, Global::MegagroupSizeMax())), KeepOtherLayers);
|
||||
}
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::itemDeselectedHook(gsl::not_null<PeerData*> peer) {
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::prepareViewHook() {
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
int AddParticipantsBoxController::alreadyInCount() const {
|
||||
return _alreadyIn.empty() ? 1 : _alreadyIn.size(); // self
|
||||
}
|
||||
|
||||
bool AddParticipantsBoxController::isAlreadyIn(gsl::not_null<UserData*> user) const {
|
||||
if (!_peer) {
|
||||
return false;
|
||||
}
|
||||
if (auto chat = _peer->asChat()) {
|
||||
return chat->participants.contains(user);
|
||||
} else if (auto channel = _peer->asChannel()) {
|
||||
return _alreadyIn.contains(user)
|
||||
|| (channel->isMegagroup() && channel->mgInfo->lastParticipants.contains(user));
|
||||
}
|
||||
Unexpected("User in AddParticipantsBoxController::isAlreadyIn");
|
||||
}
|
||||
|
||||
int AddParticipantsBoxController::fullCount() const {
|
||||
return alreadyInCount() + delegate()->peerListSelectedRowsCount();
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> AddParticipantsBoxController::createRow(gsl::not_null<UserData*> user) {
|
||||
if (user->isSelf()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto result = std::make_unique<PeerListRow>(user);
|
||||
if (isAlreadyIn(user)) {
|
||||
result->setDisabledState(PeerListRow::State::DisabledChecked);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::updateTitle() {
|
||||
auto additional = (_peer && _peer->isChannel() && !_peer->isMegagroup())
|
||||
? QString() :
|
||||
QString("%1 / %2").arg(fullCount()).arg(Global::MegagroupSizeMax());
|
||||
delegate()->peerListSetTitle(langFactory(lng_profile_add_participant));
|
||||
delegate()->peerListSetAdditionalTitle([additional] { return additional; });
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QString PeerFloodErrorText(PeerFloodType type) {
|
||||
auto link = textcmdLink(
|
||||
Messenger::Instance().createInternalLinkFull(qsl("spambot")),
|
||||
lang(lng_cant_more_info));
|
||||
if (type == PeerFloodType::InviteGroup) {
|
||||
return lng_cant_invite_not_contact(lt_more_info, link);
|
||||
}
|
||||
return lng_cant_send_to_not_contact(lt_more_info, link);
|
||||
}
|
||||
|
||||
void ShowAddContactsToChatBox(gsl::not_null<ChatData*> chat) {
|
||||
auto initBox = [chat](gsl::not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_participant_invite), [box, chat] {
|
||||
auto rows = box->peerListCollectSelectedRows();
|
||||
if (!rows.empty()) {
|
||||
auto users = std::vector<gsl::not_null<UserData*>>();
|
||||
for (auto peer : rows) {
|
||||
auto user = peer->asUser();
|
||||
t_assert(user != nullptr);
|
||||
t_assert(!user->isSelf());
|
||||
users.push_back(peer->asUser());
|
||||
}
|
||||
App::main()->addParticipants(chat, users);
|
||||
Ui::showPeerHistory(chat, ShowAtTheEndMsgId);
|
||||
}
|
||||
});
|
||||
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(chat), std::move(initBox)));
|
||||
}
|
||||
|
||||
void ShowAddContactsToChannelBox(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn,
|
||||
bool justCreated) {
|
||||
auto initBox = [channel, justCreated](gsl::not_null<PeerListBox*> box) {
|
||||
auto subscription = std::make_shared<base::Subscription>();
|
||||
box->addButton(langFactory(lng_participant_invite), [box, channel, subscription] {
|
||||
auto rows = box->peerListCollectSelectedRows();
|
||||
if (!rows.empty()) {
|
||||
auto users = std::vector<gsl::not_null<UserData*>>();
|
||||
for (auto peer : rows) {
|
||||
auto user = peer->asUser();
|
||||
t_assert(user != nullptr);
|
||||
t_assert(!user->isSelf());
|
||||
users.push_back(peer->asUser());
|
||||
}
|
||||
App::main()->addParticipants(channel, users);
|
||||
if (channel->isMegagroup()) {
|
||||
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
|
||||
} else {
|
||||
box->closeBox();
|
||||
}
|
||||
}
|
||||
});
|
||||
box->addButton(langFactory(justCreated ? lng_create_group_skip : lng_cancel), [box] { box->closeBox(); });
|
||||
if (justCreated) {
|
||||
*subscription = box->boxClosing.add_subscription([channel] {
|
||||
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
|
||||
});
|
||||
}
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(channel, std::move(alreadyIn)), std::move(initBox)));
|
||||
}
|
||||
|
||||
void ShowAddContactsToChannelBox(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn) {
|
||||
ShowAddContactsToChannelBox(channel, std::move(alreadyIn), false);
|
||||
}
|
||||
|
||||
void ShowAddContactsToChannelBox(gsl::not_null<ChannelData*> channel) {
|
||||
ShowAddContactsToChannelBox(channel, {}, true);
|
||||
}
|
||||
|
||||
class RevokePublicLinkBox::Inner : public TWidget, private MTP::Sender {
|
||||
public:
|
||||
Inner(QWidget *parent, base::lambda<void()> revokeCallback);
|
||||
@ -379,6 +575,68 @@ void GroupInfoBox::onNameSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
void GroupInfoBox::createGroup(gsl::not_null<PeerListBox*> selectUsersBox, const QString &title, const std::vector<gsl::not_null<PeerData*>> &users) {
|
||||
if (_creationRequestId) return;
|
||||
|
||||
auto inputs = QVector<MTPInputUser>();
|
||||
inputs.reserve(users.size());
|
||||
for (auto peer : users) {
|
||||
auto user = peer->asUser();
|
||||
t_assert(user != nullptr);
|
||||
if (!user->isSelf()) {
|
||||
inputs.push_back(user->inputUser);
|
||||
}
|
||||
}
|
||||
_creationRequestId = request(MTPmessages_CreateChat(MTP_vector<MTPInputUser>(inputs), MTP_string(title))).done([this](const MTPUpdates &result) {
|
||||
Ui::hideLayer();
|
||||
|
||||
App::main()->sentUpdatesReceived(result);
|
||||
|
||||
auto success = base::make_optional(&result)
|
||||
| [](auto updates) -> base::optional<const QVector<MTPChat>*> {
|
||||
switch (updates->type()) {
|
||||
case mtpc_updates:
|
||||
return &updates->c_updates().vchats.v;
|
||||
case mtpc_updatesCombined:
|
||||
return &updates->c_updatesCombined().vchats.v;
|
||||
}
|
||||
LOG(("API Error: unexpected update cons %1 (GroupInfoBox::creationDone)").arg(updates->type()));
|
||||
return base::none;
|
||||
}
|
||||
| [](auto chats) {
|
||||
return (!chats->empty() && chats->front().type() == mtpc_chat)
|
||||
? base::make_optional(chats)
|
||||
: base::none;
|
||||
}
|
||||
| [](auto chats) {
|
||||
return App::chat(chats->front().c_chat().vid.v);
|
||||
}
|
||||
| [this](gsl::not_null<ChatData*> chat) {
|
||||
if (!_photoImage.isNull()) {
|
||||
Messenger::Instance().uploadProfilePhoto(_photoImage, chat->id);
|
||||
}
|
||||
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
|
||||
};
|
||||
if (!success) {
|
||||
LOG(("API Error: chat not found in updates (ContactsBox::creationDone)"));
|
||||
}
|
||||
}).fail([this, selectUsersBox](const RPCError &error) {
|
||||
_creationRequestId = 0;
|
||||
if (error.type() == qstr("NO_CHAT_TITLE")) {
|
||||
auto guard = weak(this);
|
||||
selectUsersBox->closeBox();
|
||||
if (guard) {
|
||||
_title->showError();
|
||||
}
|
||||
} else if (error.type() == qstr("USERS_TOO_FEW")) {
|
||||
} else if (error.type() == qstr("PEER_FLOOD")) {
|
||||
Ui::show(Box<InformBox>(PeerFloodErrorText(PeerFloodType::InviteGroup)), KeepOtherLayers);
|
||||
} else if (error.type() == qstr("USER_RESTRICTED")) {
|
||||
Ui::show(Box<InformBox>(lang(lng_cant_do_this)), KeepOtherLayers);
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void GroupInfoBox::onNext() {
|
||||
if (_creationRequestId) return;
|
||||
|
||||
@ -389,64 +647,82 @@ void GroupInfoBox::onNext() {
|
||||
_title->showError();
|
||||
return;
|
||||
}
|
||||
if (_creating == CreatingGroupGroup) {
|
||||
Ui::show(Box<ContactsBox>(title, _photoImage), KeepOtherLayers);
|
||||
if (_creating != CreatingGroupGroup) {
|
||||
createChannel(title, description);
|
||||
} else {
|
||||
bool mega = false;
|
||||
auto flags = mega ? MTPchannels_CreateChannel::Flag::f_megagroup : MTPchannels_CreateChannel::Flag::f_broadcast;
|
||||
_creationRequestId = MTP::send(MTPchannels_CreateChannel(MTP_flags(flags), MTP_string(title), MTP_string(description)), rpcDone(&GroupInfoBox::creationDone), rpcFail(&GroupInfoBox::creationFail));
|
||||
auto initBox = [title, weak = weak(this)](gsl::not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_create_group_create), [box, title, weak] {
|
||||
if (weak) {
|
||||
auto rows = box->peerListCollectSelectedRows();
|
||||
if (!rows.empty()) {
|
||||
weak->createGroup(box, title, rows);
|
||||
}
|
||||
}
|
||||
});
|
||||
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(nullptr), std::move(initBox)), KeepOtherLayers);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupInfoBox::creationDone(const MTPUpdates &updates) {
|
||||
App::main()->sentUpdatesReceived(updates);
|
||||
void GroupInfoBox::createChannel(const QString &title, const QString &description) {
|
||||
bool mega = false;
|
||||
auto flags = mega ? MTPchannels_CreateChannel::Flag::f_megagroup : MTPchannels_CreateChannel::Flag::f_broadcast;
|
||||
_creationRequestId = request(MTPchannels_CreateChannel(MTP_flags(flags), MTP_string(title), MTP_string(description))).done([this](const MTPUpdates &result) {
|
||||
App::main()->sentUpdatesReceived(result);
|
||||
|
||||
const QVector<MTPChat> *v = 0;
|
||||
switch (updates.type()) {
|
||||
case mtpc_updates: v = &updates.c_updates().vchats.v; break;
|
||||
case mtpc_updatesCombined: v = &updates.c_updatesCombined().vchats.v; break;
|
||||
default: LOG(("API Error: unexpected update cons %1 (GroupInfoBox::creationDone)").arg(updates.type())); break;
|
||||
}
|
||||
|
||||
ChannelData *channel = 0;
|
||||
if (v && !v->isEmpty() && v->front().type() == mtpc_channel) {
|
||||
channel = App::channel(v->front().c_channel().vid.v);
|
||||
if (channel) {
|
||||
if (!_photoImage.isNull()) {
|
||||
Messenger::Instance().uploadProfilePhoto(_photoImage, channel->id);
|
||||
auto success = base::make_optional(&result)
|
||||
| [](auto updates) -> base::optional<const QVector<MTPChat>*> {
|
||||
switch (updates->type()) {
|
||||
case mtpc_updates:
|
||||
return &updates->c_updates().vchats.v;
|
||||
case mtpc_updatesCombined:
|
||||
return &updates->c_updatesCombined().vchats.v;
|
||||
}
|
||||
LOG(("API Error: unexpected update cons %1 (GroupInfoBox::createChannel)").arg(updates->type()));
|
||||
return base::none;
|
||||
}
|
||||
_createdChannel = channel;
|
||||
_creationRequestId = MTP::send(MTPchannels_ExportInvite(_createdChannel->inputChannel), rpcDone(&GroupInfoBox::exportDone));
|
||||
return;
|
||||
| [](auto chats) {
|
||||
return (!chats->empty() && chats->front().type() == mtpc_channel)
|
||||
? base::make_optional(chats)
|
||||
: base::none;
|
||||
}
|
||||
| [](auto chats) {
|
||||
return App::channel(chats->front().c_channel().vid.v);
|
||||
}
|
||||
| [this](gsl::not_null<ChannelData*> channel) {
|
||||
if (!_photoImage.isNull()) {
|
||||
Messenger::Instance().uploadProfilePhoto(
|
||||
_photoImage,
|
||||
channel->id);
|
||||
}
|
||||
_createdChannel = channel;
|
||||
_creationRequestId = request(
|
||||
MTPchannels_ExportInvite(_createdChannel->inputChannel)
|
||||
).done([this](const MTPExportedChatInvite &result) {
|
||||
_creationRequestId = 0;
|
||||
if (result.type() == mtpc_chatInviteExported) {
|
||||
auto link = qs(result.c_chatInviteExported().vlink);
|
||||
_createdChannel->setInviteLink(link);
|
||||
}
|
||||
Ui::show(Box<SetupChannelBox>(_createdChannel));
|
||||
}).send();
|
||||
};
|
||||
if (!success) {
|
||||
LOG(("API Error: channel not found in updates (GroupInfoBox::creationDone)"));
|
||||
closeBox();
|
||||
}
|
||||
} else {
|
||||
LOG(("API Error: channel not found in updates (GroupInfoBox::creationDone)"));
|
||||
}
|
||||
|
||||
closeBox();
|
||||
}
|
||||
|
||||
bool GroupInfoBox::creationFail(const RPCError &error) {
|
||||
if (MTP::isDefaultHandledError(error)) return false;
|
||||
|
||||
_creationRequestId = 0;
|
||||
if (error.type() == "NO_CHAT_TITLE") {
|
||||
_title->setFocus();
|
||||
_title->showError();
|
||||
return true;
|
||||
} else if (error.type() == qstr("USER_RESTRICTED")) {
|
||||
Ui::show(Box<InformBox>(lang(lng_cant_do_this)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GroupInfoBox::exportDone(const MTPExportedChatInvite &result) {
|
||||
_creationRequestId = 0;
|
||||
if (result.type() == mtpc_chatInviteExported) {
|
||||
_createdChannel->setInviteLink(qs(result.c_chatInviteExported().vlink));
|
||||
}
|
||||
Ui::show(Box<SetupChannelBox>(_createdChannel));
|
||||
}).fail([this](const RPCError &error) {
|
||||
_creationRequestId = 0;
|
||||
if (error.type() == "NO_CHAT_TITLE") {
|
||||
_title->setFocus();
|
||||
_title->showError();
|
||||
} else if (error.type() == qstr("USER_RESTRICTED")) {
|
||||
Ui::show(Box<InformBox>(lang(lng_cant_do_this)));
|
||||
} else if (error.type() == qstr("CHANNELS_TOO_MUCH")) {
|
||||
Ui::show(Box<InformBox>(lang(lng_cant_do_this))); // TODO
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void GroupInfoBox::onDescriptionResized() {
|
||||
@ -503,17 +779,13 @@ void SetupChannelBox::prepare() {
|
||||
}));
|
||||
subscribe(boxClosing, [this] {
|
||||
if (!_existing) {
|
||||
showAddContactsToChannelBox();
|
||||
ShowAddContactsToChannelBox(_channel);
|
||||
}
|
||||
});
|
||||
|
||||
updateMaxHeight();
|
||||
}
|
||||
|
||||
void SetupChannelBox::showAddContactsToChannelBox() const {
|
||||
Ui::show(Box<ContactsBox>(_channel));
|
||||
}
|
||||
|
||||
void SetupChannelBox::setInnerFocus() {
|
||||
if (_link->isHidden()) {
|
||||
setFocus();
|
||||
|
@ -24,6 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
class ConfirmBox;
|
||||
class PeerListBox;
|
||||
|
||||
namespace Ui {
|
||||
class FlatLabel;
|
||||
@ -40,6 +41,18 @@ class LinkButton;
|
||||
class NewAvatarButton;
|
||||
} // namespace Ui
|
||||
|
||||
enum class PeerFloodType {
|
||||
Send,
|
||||
InviteGroup,
|
||||
InviteChannel,
|
||||
};
|
||||
QString PeerFloodErrorText(PeerFloodType type);
|
||||
|
||||
void ShowAddContactsToChatBox(gsl::not_null<ChatData*> chat);
|
||||
void ShowAddContactsToChannelBox(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn);
|
||||
|
||||
class AddContactBox : public BoxContent, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
@ -83,7 +96,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class GroupInfoBox : public BoxContent, public RPCSender {
|
||||
class GroupInfoBox : public BoxContent, private MTP::Sender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@ -107,10 +120,8 @@ private slots:
|
||||
|
||||
private:
|
||||
void setupPhotoButton();
|
||||
|
||||
void creationDone(const MTPUpdates &updates);
|
||||
bool creationFail(const RPCError &e);
|
||||
void exportDone(const MTPExportedChatInvite &result);
|
||||
void createChannel(const QString &title, const QString &description);
|
||||
void createGroup(gsl::not_null<PeerListBox*> selectUsersBox, const QString &title, const std::vector<gsl::not_null<PeerData*>> &users);
|
||||
|
||||
void updateMaxHeight();
|
||||
void updateSelected(const QPoint &cursorGlobalPosition);
|
||||
@ -124,7 +135,7 @@ private:
|
||||
|
||||
QImage _photoImage;
|
||||
|
||||
// channel creation
|
||||
// group / channel creation
|
||||
mtpRequestId _creationRequestId = 0;
|
||||
ChannelData *_createdChannel = nullptr;
|
||||
|
||||
|
@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "styles/style_profile.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "messenger.h"
|
||||
@ -74,14 +75,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
// return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition;
|
||||
//}
|
||||
|
||||
QString PeerFloodErrorText(PeerFloodType type) {
|
||||
auto link = textcmdLink(Messenger::Instance().createInternalLinkFull(qsl("spambot")), lang(lng_cant_more_info));
|
||||
if (type == PeerFloodType::InviteGroup) {
|
||||
return lng_cant_invite_not_contact(lt_more_info, link);
|
||||
}
|
||||
return lng_cant_send_to_not_contact(lt_more_info, link);
|
||||
}
|
||||
|
||||
ContactsBox::ContactsBox(QWidget*, ChatData *chat, MembersFilter filter)
|
||||
: _chat(chat)
|
||||
, _membersFilter(filter)
|
||||
|
@ -61,19 +61,6 @@ template <typename Widget>
|
||||
class WidgetSlideWrap;
|
||||
} // namespace Ui
|
||||
|
||||
enum class PeerFloodType {
|
||||
Send,
|
||||
InviteGroup,
|
||||
InviteChannel,
|
||||
};
|
||||
QString PeerFloodErrorText(PeerFloodType type);
|
||||
|
||||
inline Ui::RoundImageCheckbox::PaintRoundImage PaintUserpicCallback(PeerData *peer) {
|
||||
return [peer](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
peer->paintUserpicLeft(p, x, y, outerWidth, size);
|
||||
};
|
||||
}
|
||||
|
||||
class ContactsBox : public BoxContent, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/effects/widget_slide_wrap.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
#include "lang/lang_keys.h"
|
||||
@ -178,7 +178,7 @@ void EditPrivacyBox::editExceptionUsers(Exception exception) {
|
||||
auto controller = std::make_unique<PrivacyExceptionsBoxController>(base::lambda_guarded(this, [this, exception] {
|
||||
return _controller->exceptionBoxTitle(exception);
|
||||
}), exceptionUsers(exception));
|
||||
auto initBox = [this, exception, controller = controller.get()](PeerListBox *box) {
|
||||
auto initBox = [this, exception, controller = controller.get()](gsl::not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_settings_save), base::lambda_guarded(this, [this, box, exception, controller] {
|
||||
exceptionUsers(exception) = controller->getResult();
|
||||
exceptionLink(exception)->entity()->setText(exceptionLinkText(exception));
|
||||
|
@ -22,22 +22,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "dialogs/dialogs_indexed_list.h"
|
||||
#include "observer_peer.h"
|
||||
#include "auth_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "storage/file_download.h"
|
||||
#include "ui/widgets/multi_select.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/effects/round_checkbox.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/effects/widget_slide_wrap.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/effects/round_checkbox.h"
|
||||
#include "boxes/contacts_box.h"
|
||||
#include "storage/file_download.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
|
||||
PeerListBox::PeerListBox(QWidget*, std::unique_ptr<PeerListController> controller, base::lambda<void(PeerListBox*)> init)
|
||||
PeerListBox::PeerListBox(QWidget*, std::unique_ptr<PeerListController> controller, base::lambda<void(gsl::not_null<PeerListBox*>)> init)
|
||||
: _controller(std::move(controller))
|
||||
, _init(std::move(init)) {
|
||||
Expects(_controller != nullptr);
|
||||
@ -221,6 +217,7 @@ void PeerListBox::peerListSetSearchMode(PeerListSearchMode mode) {
|
||||
_inner->changeCheckState(row, false, PeerListRow::SetStyle::Animated);
|
||||
update();
|
||||
}
|
||||
_controller->itemDeselectedHook(peer);
|
||||
}
|
||||
});
|
||||
_select->resizeToWidth(st::boxWideWidth);
|
||||
@ -319,8 +316,12 @@ bool PeerListBox::peerListIsRowSelected(gsl::not_null<PeerData*> peer) {
|
||||
return _select->entity()->hasItem(peer->id);
|
||||
}
|
||||
|
||||
std::vector<gsl::not_null<PeerData*>> PeerListBox::peerListCollectSelectedRows()
|
||||
{
|
||||
int PeerListBox::peerListSelectedRowsCount() {
|
||||
Expects(_select != nullptr);
|
||||
return _select->entity()->getItemsCount();
|
||||
}
|
||||
|
||||
std::vector<gsl::not_null<PeerData*>> PeerListBox::peerListCollectSelectedRows() {
|
||||
Expects(_select != nullptr);
|
||||
auto result = std::vector<gsl::not_null<PeerData*>>();
|
||||
auto items = _select->entity()->getItems();
|
||||
@ -340,7 +341,6 @@ PeerListRow::PeerListRow(gsl::not_null<PeerData*> peer, PeerListRowId id)
|
||||
: _id(id)
|
||||
, _peer(peer)
|
||||
, _initialized(false)
|
||||
, _disabled(false)
|
||||
, _isSearchResult(false) {
|
||||
}
|
||||
|
||||
@ -416,12 +416,10 @@ void PeerListRow::paintRipple(Painter &p, TimeMs ms, int x, int y, int outerWidt
|
||||
}
|
||||
|
||||
void PeerListRow::paintUserpic(Painter &p, TimeMs ms, int x, int y, int outerWidth) {
|
||||
if (_checkbox) {
|
||||
if (disabled() && checked()) {
|
||||
paintDisabledCheckUserpic(p, x, y, outerWidth);
|
||||
} else {
|
||||
_checkbox->paint(p, ms, x, y, outerWidth);
|
||||
}
|
||||
if (_disabledState == State::DisabledChecked) {
|
||||
paintDisabledCheckUserpic(p, x, y, outerWidth);
|
||||
} else if (_checkbox) {
|
||||
_checkbox->paint(p, ms, x, y, outerWidth);
|
||||
} else {
|
||||
peer()->paintUserpicLeft(p, x, y, outerWidth, st::contactsPhotoSize);
|
||||
}
|
||||
@ -898,7 +896,8 @@ void PeerListBox::Inner::paintRow(Painter &p, TimeMs ms, RowIndex index) {
|
||||
namew -= icon->width();
|
||||
icon->paint(p, namex + qMin(name.maxWidth(), namew), st::contactsPadding.top() + st::contactsNameTop, width());
|
||||
}
|
||||
p.setPen(anim::pen(st::contactsNameFg, st::contactsNameCheckedFg, row->checkedRatio()));
|
||||
auto nameCheckedRatio = row->disabled() ? 0. : row->checkedRatio();
|
||||
p.setPen(anim::pen(st::contactsNameFg, st::contactsNameCheckedFg, nameCheckedRatio));
|
||||
name.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsNameTop, namew, width());
|
||||
|
||||
if (!actionSize.isEmpty()) {
|
||||
@ -1263,177 +1262,3 @@ void PeerListBox::Inner::onPeerNameChanged(PeerData *peer, const PeerData::Names
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListRowWithLink::setActionLink(const QString &action) {
|
||||
_action = action;
|
||||
refreshActionLink();
|
||||
}
|
||||
|
||||
void PeerListRowWithLink::refreshActionLink() {
|
||||
if (!isInitialized()) return;
|
||||
_actionWidth = _action.isEmpty() ? 0 : st::normalFont->width(_action);
|
||||
}
|
||||
|
||||
void PeerListRowWithLink::lazyInitialize() {
|
||||
PeerListRow::lazyInitialize();
|
||||
refreshActionLink();
|
||||
}
|
||||
|
||||
QSize PeerListRowWithLink::actionSize() const {
|
||||
return QSize(_actionWidth, st::normalFont->height);
|
||||
}
|
||||
|
||||
QMargins PeerListRowWithLink::actionMargins() const {
|
||||
return QMargins(st::contactsCheckPosition.x(), (st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom() - st::normalFont->height) / 2, st::contactsCheckPosition.x(), 0);
|
||||
}
|
||||
|
||||
void PeerListRowWithLink::paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) {
|
||||
p.setFont(actionSelected ? st::linkOverFont : st::linkFont);
|
||||
p.setPen(actionSelected ? st::defaultLinkButton.overColor : st::defaultLinkButton.color);
|
||||
p.drawTextLeft(x, y, outerWidth, _action, _actionWidth);
|
||||
}
|
||||
|
||||
PeerListGlobalSearchController::PeerListGlobalSearchController() {
|
||||
_timer.setCallback([this] { searchOnServer(); });
|
||||
}
|
||||
|
||||
void PeerListGlobalSearchController::searchQuery(const QString &query) {
|
||||
if (_query != query) {
|
||||
_query = query;
|
||||
_requestId = 0;
|
||||
if (_query.size() >= MinUsernameLength && !searchInCache()) {
|
||||
_timer.callOnce(AutoSearchTimeout);
|
||||
} else {
|
||||
_timer.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PeerListGlobalSearchController::searchInCache() {
|
||||
auto it = _cache.find(_query);
|
||||
if (it != _cache.cend()) {
|
||||
_requestId = 0;
|
||||
searchDone(it->second, _requestId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PeerListGlobalSearchController::searchOnServer() {
|
||||
_requestId = request(MTPcontacts_Search(MTP_string(_query), MTP_int(SearchPeopleLimit))).done([this](const MTPcontacts_Found &result, mtpRequestId requestId) {
|
||||
searchDone(result, requestId);
|
||||
}).fail([this](const RPCError &error, mtpRequestId requestId) {
|
||||
if (_requestId == requestId) {
|
||||
_requestId = 0;
|
||||
delegate()->peerListSearchRefreshRows();
|
||||
}
|
||||
}).send();
|
||||
_queries.emplace(_requestId, _query);
|
||||
}
|
||||
|
||||
void PeerListGlobalSearchController::searchDone(const MTPcontacts_Found &result, mtpRequestId requestId) {
|
||||
Expects(result.type() == mtpc_contacts_found);
|
||||
|
||||
auto &contacts = result.c_contacts_found();
|
||||
auto query = _query;
|
||||
if (requestId) {
|
||||
App::feedUsers(contacts.vusers);
|
||||
App::feedChats(contacts.vchats);
|
||||
auto it = _queries.find(requestId);
|
||||
if (it != _queries.cend()) {
|
||||
query = it->second;
|
||||
_cache[query] = result;
|
||||
_queries.erase(it);
|
||||
}
|
||||
}
|
||||
if (_requestId == requestId) {
|
||||
_requestId = 0;
|
||||
for_const (auto &mtpPeer, contacts.vresults.v) {
|
||||
if (auto peer = App::peerLoaded(peerFromMTP(mtpPeer))) {
|
||||
delegate()->peerListSearchAddRow(peer);
|
||||
}
|
||||
}
|
||||
delegate()->peerListSearchRefreshRows();
|
||||
}
|
||||
}
|
||||
|
||||
bool PeerListGlobalSearchController::isLoading() {
|
||||
return _timer.isActive() || _requestId;
|
||||
}
|
||||
|
||||
ChatsListBoxController::ChatsListBoxController(std::unique_ptr<PeerListSearchController> searchController) : PeerListController(std::move(searchController)) {
|
||||
}
|
||||
|
||||
void ChatsListBoxController::prepare() {
|
||||
setSearchNoResultsText(lang(lng_blocked_list_not_found));
|
||||
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
|
||||
|
||||
prepareViewHook();
|
||||
|
||||
rebuildRows();
|
||||
|
||||
auto &sessionData = Auth().data();
|
||||
subscribe(sessionData.contactsLoaded(), [this](bool loaded) {
|
||||
rebuildRows();
|
||||
});
|
||||
subscribe(sessionData.moreChatsLoaded(), [this] {
|
||||
rebuildRows();
|
||||
});
|
||||
subscribe(sessionData.allChatsLoaded(), [this](bool loaded) {
|
||||
checkForEmptyRows();
|
||||
});
|
||||
}
|
||||
|
||||
void ChatsListBoxController::rebuildRows() {
|
||||
auto ms = getms();
|
||||
auto wasEmpty = !delegate()->peerListFullRowsCount();
|
||||
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) {
|
||||
// Place dialogs list before contactsNoDialogs list.
|
||||
delegate()->peerListPartitionRows([](PeerListRow &a) {
|
||||
auto history = static_cast<Row&>(a).history();
|
||||
return history->inChatList(Dialogs::Mode::All);
|
||||
});
|
||||
}
|
||||
checkForEmptyRows();
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
|
||||
void ChatsListBoxController::checkForEmptyRows() {
|
||||
if (delegate()->peerListFullRowsCount()) {
|
||||
setDescriptionText(QString());
|
||||
} else {
|
||||
auto &sessionData = Auth().data();
|
||||
auto loaded = sessionData.contactsLoaded().value() && sessionData.allChatsLoaded().value();
|
||||
setDescriptionText(lang(loaded ? lng_contacts_not_found : lng_contacts_loading));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> ChatsListBoxController::createSearchRow(gsl::not_null<PeerData*> peer) {
|
||||
return createRow(App::history(peer));
|
||||
}
|
||||
|
||||
bool ChatsListBoxController::appendRow(History *history) {
|
||||
if (auto row = delegate()->peerListFindRow(history->peer->id)) {
|
||||
updateRowHook(static_cast<Row*>(row));
|
||||
return false;
|
||||
}
|
||||
if (auto row = createRow(history)) {
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -33,14 +33,25 @@ class WidgetSlideWrap;
|
||||
class FlatLabel;
|
||||
} // namespace Ui
|
||||
|
||||
inline auto PaintUserpicCallback(PeerData *peer) {
|
||||
return [peer](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
peer->paintUserpicLeft(p, x, y, outerWidth, size);
|
||||
};
|
||||
}
|
||||
|
||||
using PeerListRowId = uint64;
|
||||
class PeerListRow {
|
||||
public:
|
||||
PeerListRow(gsl::not_null<PeerData*> peer);
|
||||
PeerListRow(gsl::not_null<PeerData*> peer, PeerListRowId id);
|
||||
|
||||
void setDisabled(bool disabled) {
|
||||
_disabled = disabled;
|
||||
enum class State {
|
||||
Active,
|
||||
Disabled,
|
||||
DisabledChecked,
|
||||
};
|
||||
void setDisabledState(State state) {
|
||||
_disabledState = state;
|
||||
}
|
||||
|
||||
// Checked state is controlled by the box with multiselect,
|
||||
@ -97,7 +108,7 @@ public:
|
||||
return _absoluteIndex;
|
||||
}
|
||||
bool disabled() const {
|
||||
return _disabled;
|
||||
return (_disabledState != State::Active);
|
||||
}
|
||||
bool isSearchResult() const {
|
||||
return _isSearchResult;
|
||||
@ -156,8 +167,8 @@ private:
|
||||
StatusType _statusType = StatusType::Online;
|
||||
OrderedSet<QChar> _nameFirstChars;
|
||||
int _absoluteIndex = -1;
|
||||
State _disabledState = State::Active;
|
||||
bool _initialized : 1;
|
||||
bool _disabled : 1;
|
||||
bool _isSearchResult : 1;
|
||||
|
||||
};
|
||||
@ -170,6 +181,7 @@ enum class PeerListSearchMode {
|
||||
class PeerListDelegate {
|
||||
public:
|
||||
virtual void peerListSetTitle(base::lambda<QString()> title) = 0;
|
||||
virtual void peerListSetAdditionalTitle(base::lambda<QString()> title) = 0;
|
||||
virtual void peerListSetDescription(object_ptr<Ui::FlatLabel> description) = 0;
|
||||
virtual void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) = 0;
|
||||
virtual void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) = 0;
|
||||
@ -200,6 +212,7 @@ public:
|
||||
peerListFinishSelectedRowsBunch();
|
||||
}
|
||||
|
||||
virtual int peerListSelectedRowsCount() = 0;
|
||||
virtual std::vector<gsl::not_null<PeerData*>> peerListCollectSelectedRows() = 0;
|
||||
virtual ~PeerListDelegate() = default;
|
||||
|
||||
@ -254,6 +267,8 @@ public:
|
||||
}
|
||||
virtual void loadMoreRows() {
|
||||
}
|
||||
virtual void itemDeselectedHook(gsl::not_null<PeerData*> peer) {
|
||||
}
|
||||
bool isSearchLoading() const {
|
||||
return _searchController ? _searchController->isLoading() : false;
|
||||
}
|
||||
@ -305,11 +320,14 @@ private:
|
||||
|
||||
class PeerListBox : public BoxContent, public PeerListDelegate {
|
||||
public:
|
||||
PeerListBox(QWidget*, std::unique_ptr<PeerListController> controller, base::lambda<void(PeerListBox*)> init);
|
||||
PeerListBox(QWidget*, std::unique_ptr<PeerListController> controller, base::lambda<void(gsl::not_null<PeerListBox*>)> init);
|
||||
|
||||
void peerListSetTitle(base::lambda<QString()> title) override {
|
||||
setTitle(std::move(title));
|
||||
}
|
||||
void peerListSetAdditionalTitle(base::lambda<QString()> title) override {
|
||||
setAdditionalTitle(std::move(title));
|
||||
}
|
||||
void peerListSetDescription(object_ptr<Ui::FlatLabel> description) override;
|
||||
void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) override;
|
||||
void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) override;
|
||||
@ -325,6 +343,7 @@ public:
|
||||
void peerListSetRowChecked(gsl::not_null<PeerListRow*> row, bool checked) override;
|
||||
gsl::not_null<PeerListRow*> peerListRowAt(int index) override;
|
||||
bool peerListIsRowSelected(gsl::not_null<PeerData*> peer) override;
|
||||
int peerListSelectedRowsCount() override;
|
||||
std::vector<gsl::not_null<PeerData*>> peerListCollectSelectedRows() override;
|
||||
void peerListRefreshRows() override;
|
||||
void peerListScrollToTop() override;
|
||||
@ -530,77 +549,3 @@ private:
|
||||
std::vector<std::unique_ptr<PeerListRow>> _searchRows;
|
||||
|
||||
};
|
||||
|
||||
class PeerListRowWithLink : public PeerListRow {
|
||||
public:
|
||||
using PeerListRow::PeerListRow;
|
||||
|
||||
void setActionLink(const QString &action);
|
||||
|
||||
void lazyInitialize() override;
|
||||
|
||||
private:
|
||||
void refreshActionLink();
|
||||
QSize actionSize() const override;
|
||||
QMargins actionMargins() const override;
|
||||
void paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) override;
|
||||
|
||||
QString _action;
|
||||
int _actionWidth = 0;
|
||||
|
||||
};
|
||||
|
||||
class PeerListGlobalSearchController : public PeerListSearchController, private MTP::Sender {
|
||||
public:
|
||||
PeerListGlobalSearchController();
|
||||
|
||||
void searchQuery(const QString &query) override;
|
||||
bool isLoading() override;
|
||||
bool loadMoreRows() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool searchInCache();
|
||||
void searchOnServer();
|
||||
void searchDone(const MTPcontacts_Found &result, mtpRequestId requestId);
|
||||
|
||||
base::Timer _timer;
|
||||
QString _query;
|
||||
mtpRequestId _requestId = 0;
|
||||
std::map<QString, MTPcontacts_Found> _cache;
|
||||
std::map<mtpRequestId, QString> _queries;
|
||||
|
||||
};
|
||||
|
||||
class ChatsListBoxController : public PeerListController, protected base::Subscriber {
|
||||
public:
|
||||
ChatsListBoxController(std::unique_ptr<PeerListSearchController> searchController = std::make_unique<PeerListGlobalSearchController>());
|
||||
|
||||
void prepare() override final;
|
||||
std::unique_ptr<PeerListRow> createSearchRow(gsl::not_null<PeerData*> peer) override final;
|
||||
|
||||
protected:
|
||||
class Row : public PeerListRow {
|
||||
public:
|
||||
Row(gsl::not_null<History*> history) : PeerListRow(history->peer), _history(history) {
|
||||
}
|
||||
gsl::not_null<History*> history() const {
|
||||
return _history;
|
||||
}
|
||||
|
||||
private:
|
||||
gsl::not_null<History*> _history;
|
||||
|
||||
};
|
||||
virtual std::unique_ptr<Row> createRow(gsl::not_null<History*> history) = 0;
|
||||
virtual void prepareViewHook() = 0;
|
||||
virtual void updateRowHook(Row *row) {
|
||||
}
|
||||
|
||||
private:
|
||||
void rebuildRows();
|
||||
void checkForEmptyRows();
|
||||
bool appendRow(History *history);
|
||||
|
||||
};
|
||||
|
270
Telegram/SourceFiles/boxes/peer_list_controllers.cpp
Normal file
270
Telegram/SourceFiles/boxes/peer_list_controllers.cpp
Normal file
@ -0,0 +1,270 @@
|
||||
/*
|
||||
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 "boxes/peer_list_controllers.h"
|
||||
|
||||
#include "styles/style_boxes.h"
|
||||
#include "auth_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "dialogs/dialogs_indexed_list.h"
|
||||
|
||||
void PeerListRowWithLink::setActionLink(const QString &action) {
|
||||
_action = action;
|
||||
refreshActionLink();
|
||||
}
|
||||
|
||||
void PeerListRowWithLink::refreshActionLink() {
|
||||
if (!isInitialized()) return;
|
||||
_actionWidth = _action.isEmpty() ? 0 : st::normalFont->width(_action);
|
||||
}
|
||||
|
||||
void PeerListRowWithLink::lazyInitialize() {
|
||||
PeerListRow::lazyInitialize();
|
||||
refreshActionLink();
|
||||
}
|
||||
|
||||
QSize PeerListRowWithLink::actionSize() const {
|
||||
return QSize(_actionWidth, st::normalFont->height);
|
||||
}
|
||||
|
||||
QMargins PeerListRowWithLink::actionMargins() const {
|
||||
return QMargins(st::contactsCheckPosition.x(), (st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom() - st::normalFont->height) / 2, st::contactsCheckPosition.x(), 0);
|
||||
}
|
||||
|
||||
void PeerListRowWithLink::paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) {
|
||||
p.setFont(actionSelected ? st::linkOverFont : st::linkFont);
|
||||
p.setPen(actionSelected ? st::defaultLinkButton.overColor : st::defaultLinkButton.color);
|
||||
p.drawTextLeft(x, y, outerWidth, _action, _actionWidth);
|
||||
}
|
||||
|
||||
PeerListGlobalSearchController::PeerListGlobalSearchController() {
|
||||
_timer.setCallback([this] { searchOnServer(); });
|
||||
}
|
||||
|
||||
void PeerListGlobalSearchController::searchQuery(const QString &query) {
|
||||
if (_query != query) {
|
||||
_query = query;
|
||||
_requestId = 0;
|
||||
if (_query.size() >= MinUsernameLength && !searchInCache()) {
|
||||
_timer.callOnce(AutoSearchTimeout);
|
||||
} else {
|
||||
_timer.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PeerListGlobalSearchController::searchInCache() {
|
||||
auto it = _cache.find(_query);
|
||||
if (it != _cache.cend()) {
|
||||
_requestId = 0;
|
||||
searchDone(it->second, _requestId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PeerListGlobalSearchController::searchOnServer() {
|
||||
_requestId = request(MTPcontacts_Search(MTP_string(_query), MTP_int(SearchPeopleLimit))).done([this](const MTPcontacts_Found &result, mtpRequestId requestId) {
|
||||
searchDone(result, requestId);
|
||||
}).fail([this](const RPCError &error, mtpRequestId requestId) {
|
||||
if (_requestId == requestId) {
|
||||
_requestId = 0;
|
||||
delegate()->peerListSearchRefreshRows();
|
||||
}
|
||||
}).send();
|
||||
_queries.emplace(_requestId, _query);
|
||||
}
|
||||
|
||||
void PeerListGlobalSearchController::searchDone(const MTPcontacts_Found &result, mtpRequestId requestId) {
|
||||
Expects(result.type() == mtpc_contacts_found);
|
||||
|
||||
auto &contacts = result.c_contacts_found();
|
||||
auto query = _query;
|
||||
if (requestId) {
|
||||
App::feedUsers(contacts.vusers);
|
||||
App::feedChats(contacts.vchats);
|
||||
auto it = _queries.find(requestId);
|
||||
if (it != _queries.cend()) {
|
||||
query = it->second;
|
||||
_cache[query] = result;
|
||||
_queries.erase(it);
|
||||
}
|
||||
}
|
||||
if (_requestId == requestId) {
|
||||
_requestId = 0;
|
||||
for_const (auto &mtpPeer, contacts.vresults.v) {
|
||||
if (auto peer = App::peerLoaded(peerFromMTP(mtpPeer))) {
|
||||
delegate()->peerListSearchAddRow(peer);
|
||||
}
|
||||
}
|
||||
delegate()->peerListSearchRefreshRows();
|
||||
}
|
||||
}
|
||||
|
||||
bool PeerListGlobalSearchController::isLoading() {
|
||||
return _timer.isActive() || _requestId;
|
||||
}
|
||||
|
||||
ChatsListBoxController::ChatsListBoxController(std::unique_ptr<PeerListSearchController> searchController) : PeerListController(std::move(searchController)) {
|
||||
}
|
||||
|
||||
void ChatsListBoxController::prepare() {
|
||||
setSearchNoResultsText(lang(lng_blocked_list_not_found));
|
||||
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
|
||||
|
||||
prepareViewHook();
|
||||
|
||||
rebuildRows();
|
||||
|
||||
auto &sessionData = Auth().data();
|
||||
subscribe(sessionData.contactsLoaded(), [this](bool loaded) {
|
||||
rebuildRows();
|
||||
});
|
||||
subscribe(sessionData.moreChatsLoaded(), [this] {
|
||||
rebuildRows();
|
||||
});
|
||||
subscribe(sessionData.allChatsLoaded(), [this](bool loaded) {
|
||||
checkForEmptyRows();
|
||||
});
|
||||
}
|
||||
|
||||
void ChatsListBoxController::rebuildRows() {
|
||||
auto wasEmpty = !delegate()->peerListFullRowsCount();
|
||||
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) {
|
||||
// Place dialogs list before contactsNoDialogs list.
|
||||
delegate()->peerListPartitionRows([](PeerListRow &a) {
|
||||
auto history = static_cast<Row&>(a).history();
|
||||
return history->inChatList(Dialogs::Mode::All);
|
||||
});
|
||||
}
|
||||
checkForEmptyRows();
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
|
||||
void ChatsListBoxController::checkForEmptyRows() {
|
||||
if (delegate()->peerListFullRowsCount()) {
|
||||
setDescriptionText(QString());
|
||||
} else {
|
||||
auto &sessionData = Auth().data();
|
||||
auto loaded = sessionData.contactsLoaded().value() && sessionData.allChatsLoaded().value();
|
||||
setDescriptionText(lang(loaded ? lng_contacts_not_found : lng_contacts_loading));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> ChatsListBoxController::createSearchRow(gsl::not_null<PeerData*> peer) {
|
||||
return createRow(App::history(peer));
|
||||
}
|
||||
|
||||
bool ChatsListBoxController::appendRow(gsl::not_null<History*> history) {
|
||||
if (auto row = delegate()->peerListFindRow(history->peer->id)) {
|
||||
updateRowHook(static_cast<Row*>(row));
|
||||
return false;
|
||||
}
|
||||
if (auto row = createRow(history)) {
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ContactsBoxController::ContactsBoxController(std::unique_ptr<PeerListSearchController> searchController) : PeerListController(std::move(searchController)) {
|
||||
}
|
||||
|
||||
void ContactsBoxController::prepare() {
|
||||
setSearchNoResultsText(lang(lng_blocked_list_not_found));
|
||||
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
|
||||
delegate()->peerListSetTitle(langFactory(lng_contacts_header));
|
||||
|
||||
prepareViewHook();
|
||||
|
||||
rebuildRows();
|
||||
|
||||
auto &sessionData = Auth().data();
|
||||
subscribe(sessionData.contactsLoaded(), [this](bool loaded) {
|
||||
rebuildRows();
|
||||
});
|
||||
}
|
||||
|
||||
void ContactsBoxController::rebuildRows() {
|
||||
auto appendList = [this](auto chats) {
|
||||
auto count = 0;
|
||||
for_const (auto row, chats->all()) {
|
||||
auto history = row->history();
|
||||
if (auto user = history->peer->asUser()) {
|
||||
if (appendRow(user)) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
};
|
||||
appendList(App::main()->contactsList());
|
||||
checkForEmptyRows();
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
|
||||
void ContactsBoxController::checkForEmptyRows() {
|
||||
if (delegate()->peerListFullRowsCount()) {
|
||||
setDescriptionText(QString());
|
||||
} else {
|
||||
auto &sessionData = Auth().data();
|
||||
auto loaded = sessionData.contactsLoaded().value();
|
||||
setDescriptionText(lang(loaded ? lng_contacts_not_found : lng_contacts_loading));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> ContactsBoxController::createSearchRow(gsl::not_null<PeerData*> peer) {
|
||||
return createRow(peer->asUser());
|
||||
}
|
||||
|
||||
void ContactsBoxController::rowClicked(gsl::not_null<PeerListRow*> row) {
|
||||
Ui::showPeerHistory(row->peer(), ShowAtUnreadMsgId);
|
||||
}
|
||||
|
||||
bool ContactsBoxController::appendRow(gsl::not_null<UserData*> user) {
|
||||
if (auto row = delegate()->peerListFindRow(user->id)) {
|
||||
updateRowHook(row);
|
||||
return false;
|
||||
}
|
||||
if (auto row = createRow(user)) {
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> ContactsBoxController::createRow(gsl::not_null<UserData*> user) {
|
||||
return std::make_unique<PeerListRow>(user);
|
||||
}
|
119
Telegram/SourceFiles/boxes/peer_list_controllers.h
Normal file
119
Telegram/SourceFiles/boxes/peer_list_controllers.h
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
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 "peer_list_box.h"
|
||||
|
||||
class PeerListRowWithLink : public PeerListRow {
|
||||
public:
|
||||
using PeerListRow::PeerListRow;
|
||||
|
||||
void setActionLink(const QString &action);
|
||||
|
||||
void lazyInitialize() override;
|
||||
|
||||
private:
|
||||
void refreshActionLink();
|
||||
QSize actionSize() const override;
|
||||
QMargins actionMargins() const override;
|
||||
void paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) override;
|
||||
|
||||
QString _action;
|
||||
int _actionWidth = 0;
|
||||
|
||||
};
|
||||
|
||||
class PeerListGlobalSearchController : public PeerListSearchController, private MTP::Sender {
|
||||
public:
|
||||
PeerListGlobalSearchController();
|
||||
|
||||
void searchQuery(const QString &query) override;
|
||||
bool isLoading() override;
|
||||
bool loadMoreRows() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool searchInCache();
|
||||
void searchOnServer();
|
||||
void searchDone(const MTPcontacts_Found &result, mtpRequestId requestId);
|
||||
|
||||
base::Timer _timer;
|
||||
QString _query;
|
||||
mtpRequestId _requestId = 0;
|
||||
std::map<QString, MTPcontacts_Found> _cache;
|
||||
std::map<mtpRequestId, QString> _queries;
|
||||
|
||||
};
|
||||
|
||||
class ChatsListBoxController : public PeerListController, protected base::Subscriber {
|
||||
public:
|
||||
ChatsListBoxController(std::unique_ptr<PeerListSearchController> searchController = std::make_unique<PeerListGlobalSearchController>());
|
||||
|
||||
void prepare() override final;
|
||||
std::unique_ptr<PeerListRow> createSearchRow(gsl::not_null<PeerData*> peer) override final;
|
||||
|
||||
protected:
|
||||
class Row : public PeerListRow {
|
||||
public:
|
||||
Row(gsl::not_null<History*> history) : PeerListRow(history->peer), _history(history) {
|
||||
}
|
||||
gsl::not_null<History*> history() const {
|
||||
return _history;
|
||||
}
|
||||
|
||||
private:
|
||||
gsl::not_null<History*> _history;
|
||||
|
||||
};
|
||||
virtual std::unique_ptr<Row> createRow(gsl::not_null<History*> history) = 0;
|
||||
virtual void prepareViewHook() = 0;
|
||||
virtual void updateRowHook(gsl::not_null<Row*> row) {
|
||||
}
|
||||
|
||||
private:
|
||||
void rebuildRows();
|
||||
void checkForEmptyRows();
|
||||
bool appendRow(gsl::not_null<History*> history);
|
||||
|
||||
};
|
||||
|
||||
class ContactsBoxController : public PeerListController, protected base::Subscriber {
|
||||
public:
|
||||
ContactsBoxController(std::unique_ptr<PeerListSearchController> searchController = std::make_unique<PeerListGlobalSearchController>());
|
||||
|
||||
void prepare() override final;
|
||||
std::unique_ptr<PeerListRow> createSearchRow(gsl::not_null<PeerData*> peer) override final;
|
||||
void rowClicked(gsl::not_null<PeerListRow*> row) override;
|
||||
|
||||
protected:
|
||||
virtual std::unique_ptr<PeerListRow> createRow(gsl::not_null<UserData*> user);
|
||||
virtual void prepareViewHook() {
|
||||
}
|
||||
virtual void updateRowHook(gsl::not_null<PeerListRow*> row) {
|
||||
}
|
||||
|
||||
private:
|
||||
void rebuildRows();
|
||||
void checkForEmptyRows();
|
||||
bool appendRow(gsl::not_null<UserData*> user);
|
||||
|
||||
};
|
@ -38,7 +38,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "boxes/contacts_box.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "auth_session.h"
|
||||
#include "messenger.h"
|
||||
|
||||
|
@ -617,7 +617,7 @@ enum ShowLayerOption {
|
||||
AnimatedShowLayer = 0x00,
|
||||
ForceFastShowLayer = 0x04,
|
||||
};
|
||||
Q_DECLARE_FLAGS(ShowLayerOptions, ShowLayerOption);
|
||||
using ShowLayerOptions = QFlags<ShowLayerOption>;
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(ShowLayerOptions);
|
||||
|
||||
static int32 FullArcLength = 360 * 16;
|
||||
|
@ -181,7 +181,7 @@ private:
|
||||
MessageSearch = 0x08,
|
||||
All = 0x0F,
|
||||
};
|
||||
Q_DECLARE_FLAGS(UpdateRowSections, UpdateRowSection);
|
||||
using UpdateRowSections = QFlags<UpdateRowSection>;
|
||||
Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(UpdateRowSections);
|
||||
void updateDialogRow(PeerData *peer, MsgId msgId, QRect updateRect, UpdateRowSections sections = UpdateRowSection::All);
|
||||
|
||||
|
@ -545,7 +545,7 @@ private:
|
||||
f_has_pending_resized_items = (1 << 0),
|
||||
f_pending_resize = (1 << 1),
|
||||
};
|
||||
Q_DECLARE_FLAGS(Flags, Flag);
|
||||
using Flags = QFlags<Flag>;
|
||||
Q_DECL_CONSTEXPR friend inline QFlags<Flags::enum_type> operator|(Flags::enum_type f1, Flags::enum_type f2) noexcept {
|
||||
return QFlags<Flags::enum_type>(f1) | f2;
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply> {
|
||||
PaintInBubble = 0x01,
|
||||
PaintSelected = 0x02,
|
||||
};
|
||||
Q_DECLARE_FLAGS(PaintFlags, PaintFlag);
|
||||
using PaintFlags = QFlags<PaintFlag>;
|
||||
void paint(Painter &p, const HistoryItem *holder, int x, int y, int w, PaintFlags flags) const;
|
||||
|
||||
MsgId replyToId() const {
|
||||
|
@ -689,7 +689,7 @@ private:
|
||||
SaveDraft = 0x01,
|
||||
SendTyping = 0x02,
|
||||
};
|
||||
Q_DECLARE_FLAGS(TextUpdateEvents, TextUpdateEvent);
|
||||
using TextUpdateEvents = QFlags<TextUpdateEvent>;
|
||||
Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(TextUpdateEvents);
|
||||
|
||||
void writeDrafts(Data::Draft **localDraft, Data::Draft **editDraft);
|
||||
|
@ -72,7 +72,7 @@ struct PeerUpdate {
|
||||
ChannelRightsChanged = 0x00020000U,
|
||||
ChannelStickersChanged = 0x00040000U,
|
||||
};
|
||||
Q_DECLARE_FLAGS(Flags, Flag);
|
||||
using Flags = QFlags<Flag>;
|
||||
Flags flags = 0;
|
||||
|
||||
// NameChanged data
|
||||
|
@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "window/notifications_manager_default.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
#include "boxes/contacts_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/about_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "platform/mac/mac_utilities.h"
|
||||
@ -437,7 +438,10 @@ void MainWindow::createGlobalMenu() {
|
||||
if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray();
|
||||
|
||||
if (!App::self()) return;
|
||||
Ui::show(Box<ContactsBox>());
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<ContactsBoxController>(), [](gsl::not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_close), [box] { box->closeBox(); });
|
||||
box->addLeftButton(langFactory(lng_profile_add_contact), [] { App::wnd()->onShowAddContact(); });
|
||||
}));
|
||||
});
|
||||
psAddContact = window->addAction(lang(lng_mac_menu_add_contact), App::wnd(), SLOT(onShowAddContact()));
|
||||
window->addSeparator();
|
||||
|
@ -60,7 +60,7 @@ public:
|
||||
Hidden = 0x08,
|
||||
Activate = 0x10,
|
||||
};
|
||||
Q_DECLARE_FLAGS(ShadowsChanges, ShadowsChange);
|
||||
using ShadowsChanges = QFlags<ShadowsChange>;
|
||||
|
||||
bool shadowsWorking() const {
|
||||
return _shadowsWorking;
|
||||
|
@ -20,8 +20,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "profile/profile_channel_controllers.h"
|
||||
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/edit_participant_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "boxes/contacts_box.h"
|
||||
#include "auth_session.h"
|
||||
#include "apiwrap.h"
|
||||
@ -55,7 +57,7 @@ std::unique_ptr<PeerListSearchController> ParticipantsBoxController::CreateSearc
|
||||
|
||||
void ParticipantsBoxController::Start(gsl::not_null<ChannelData*> channel, Role role) {
|
||||
auto controller = std::make_unique<ParticipantsBoxController>(channel, role);
|
||||
auto initBox = [role, channel, controller = controller.get()](PeerListBox *box) {
|
||||
auto initBox = [role, channel, controller = controller.get()](gsl::not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_close), [box] { box->closeBox(); });
|
||||
auto canAddNewItem = [role, channel] {
|
||||
switch (role) {
|
||||
@ -87,12 +89,12 @@ void ParticipantsBoxController::addNewItem() {
|
||||
if (_channel->membersCount() >= Global::ChatSizeMax()) {
|
||||
Ui::show(Box<MaxInviteBox>(_channel), KeepOtherLayers);
|
||||
} else {
|
||||
auto already = MembersAlreadyIn();
|
||||
auto already = std::vector<gsl::not_null<UserData*>>();
|
||||
already.reserve(delegate()->peerListFullRowsCount());
|
||||
for (auto i = 0, count = delegate()->peerListFullRowsCount(); i != count; ++i) {
|
||||
auto user = delegate()->peerListRowAt(i)->peer()->asUser();
|
||||
already.insert(user);
|
||||
already.push_back(delegate()->peerListRowAt(i)->peer()->asUser());
|
||||
}
|
||||
Ui::show(Box<ContactsBox>(_channel, MembersFilter::Recent, already));
|
||||
ShowAddContactsToChannelBox(_channel, { already.begin(), already.end() });
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -105,7 +107,7 @@ void ParticipantsBoxController::addNewItem() {
|
||||
if (weak) {
|
||||
weak->editRestrictedDone(user, rights);
|
||||
}
|
||||
}), [](PeerListBox *box) {
|
||||
}), [](gsl::not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
|
||||
}), KeepOtherLayers);
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/contacts_box.h"
|
||||
#include "boxes/photo_crop_box.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
@ -538,14 +539,11 @@ void CoverWidget::onAddMember() {
|
||||
if (_peerChat->count >= Global::ChatSizeMax() && _peerChat->amCreator()) {
|
||||
Ui::show(Box<ConvertToSupergroupBox>(_peerChat));
|
||||
} else {
|
||||
Ui::show(Box<ContactsBox>(_peerChat, MembersFilter::Recent));
|
||||
ShowAddContactsToChatBox(_peerChat);
|
||||
}
|
||||
} else if (_peerChannel && _peerChannel->mgInfo) {
|
||||
MembersAlreadyIn already;
|
||||
for_const (auto user, _peerChannel->mgInfo->lastParticipants) {
|
||||
already.insert(user);
|
||||
}
|
||||
Ui::show(Box<ContactsBox>(_peerChannel, MembersFilter::Recent, already));
|
||||
auto &participants = _peerChannel->mgInfo->lastParticipants;
|
||||
ShowAddContactsToChannelBox(_peerChannel, { participants.cbegin(), participants.cend() });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "mainwidget.h"
|
||||
#include "auth_session.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
|
||||
namespace Settings {
|
||||
@ -44,7 +45,7 @@ public:
|
||||
protected:
|
||||
void prepareViewHook() override;
|
||||
std::unique_ptr<Row> createRow(gsl::not_null<History*> history) override;
|
||||
void updateRowHook(Row *row) override {
|
||||
void updateRowHook(gsl::not_null<Row*> row) override {
|
||||
updateIsBlocked(row, row->peer()->asUser());
|
||||
delegate()->peerListUpdateRow(row);
|
||||
}
|
||||
@ -70,7 +71,7 @@ void BlockUserBoxController::prepareViewHook() {
|
||||
|
||||
void BlockUserBoxController::updateIsBlocked(gsl::not_null<PeerListRow*> row, UserData *user) const {
|
||||
auto blocked = user->isBlocked();
|
||||
row->setDisabled(blocked);
|
||||
row->setDisabledState(blocked ? PeerListRow::State::DisabledChecked : PeerListRow::State::Active);
|
||||
if (blocked) {
|
||||
row->setCustomStatus(lang(lng_blocked_list_already_blocked));
|
||||
} else {
|
||||
@ -186,7 +187,7 @@ void BlockedBoxController::handleBlockedEvent(UserData *user) {
|
||||
|
||||
void BlockedBoxController::BlockNewUser() {
|
||||
auto controller = std::make_unique<BlockUserBoxController>();
|
||||
auto initBox = [controller = controller.get()](PeerListBox *box) {
|
||||
auto initBox = [controller = controller.get()](gsl::not_null<PeerListBox*> box) {
|
||||
controller->setBlockUserCallback([box](gsl::not_null<UserData*> user) {
|
||||
Auth().api().blockUser(user);
|
||||
box->closeBox();
|
||||
|
@ -208,7 +208,7 @@ void PrivacyWidget::autoLockUpdated() {
|
||||
}
|
||||
|
||||
void PrivacyWidget::onBlockedUsers() {
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<BlockedBoxController>(), [](PeerListBox *box) {
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<BlockedBoxController>(), [](gsl::not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_close), [box] { box->closeBox(); });
|
||||
box->addLeftButton(langFactory(lng_blocked_list_add), [box] { BlockedBoxController::BlockNewUser(); });
|
||||
}));
|
||||
|
@ -78,7 +78,7 @@ enum class FileOption {
|
||||
User = 0x01,
|
||||
Safe = 0x02,
|
||||
};
|
||||
Q_DECLARE_FLAGS(FileOptions, FileOption);
|
||||
using FileOptions = QFlags<FileOption>;
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(FileOptions);
|
||||
|
||||
bool keyAlreadyUsed(QString &name, FileOptions options = FileOption::User | FileOption::Safe) {
|
||||
|
@ -80,7 +80,7 @@ protected:
|
||||
Down = 0x02,
|
||||
Disabled = 0x04,
|
||||
};
|
||||
Q_DECLARE_FLAGS(State, StateFlag);
|
||||
using State = QFlags<StateFlag>;
|
||||
Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(State);
|
||||
|
||||
State state() const {
|
||||
|
@ -47,7 +47,7 @@ enum class ImageRoundCorner {
|
||||
BottomRight = 0x08,
|
||||
All = 0x0f,
|
||||
};
|
||||
Q_DECLARE_FLAGS(ImageRoundCorners, ImageRoundCorner);
|
||||
using ImageRoundCorners = QFlags<ImageRoundCorner>;
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(ImageRoundCorners);
|
||||
|
||||
inline uint32 packInt(int32 a) {
|
||||
@ -199,7 +199,7 @@ enum class Option {
|
||||
Colored = 0x200,
|
||||
TransparentBackground = 0x400,
|
||||
};
|
||||
Q_DECLARE_FLAGS(Options, Option);
|
||||
using Options = QFlags<Option>;
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Options);
|
||||
|
||||
QImage prepare(QImage img, int w, int h, Options options, int outerw, int outerh, const style::color *colored = nullptr);
|
||||
|
@ -132,7 +132,7 @@ public:
|
||||
LookupSymbol = 0x02,
|
||||
LookupLink = 0x04,
|
||||
};
|
||||
Q_DECLARE_FLAGS(Flags, Flag);
|
||||
using Flags = QFlags<Flag>;
|
||||
|
||||
StateRequest() {
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ enum class RectPart {
|
||||
|
||||
Full = FullTop | NoTop,
|
||||
};
|
||||
Q_DECLARE_FLAGS(RectParts, RectPart);
|
||||
using RectParts = QFlags<RectPart>;
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(RectParts);
|
||||
|
||||
inline bool IsTopCorner(RectPart corner) {
|
||||
|
@ -30,7 +30,7 @@ enum class GifPauseReason {
|
||||
RoundPlaying = (1 << 3),
|
||||
MediaPreview = (1 << 4),
|
||||
};
|
||||
Q_DECLARE_FLAGS(GifPauseReasons, GifPauseReason);
|
||||
using GifPauseReasons = QFlags<GifPauseReason>;
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(GifPauseReasons);
|
||||
|
||||
class MainWindow;
|
||||
|
@ -31,7 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "storage/localstorage.h"
|
||||
#include "boxes/contacts_box.h"
|
||||
#include "boxes/about_box.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "calls/calls_box_controller.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "core/click_handler_types.h"
|
||||
@ -97,11 +97,14 @@ void MainMenu::refreshMenu() {
|
||||
App::wnd()->onShowNewChannel();
|
||||
}, &st::mainMenuNewChannel, &st::mainMenuNewChannelOver);
|
||||
_menu->addAction(lang(lng_menu_contacts), [] {
|
||||
Ui::show(Box<ContactsBox>());
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<ContactsBoxController>(), [](gsl::not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_close), [box] { box->closeBox(); });
|
||||
box->addLeftButton(langFactory(lng_profile_add_contact), [] { App::wnd()->onShowAddContact(); });
|
||||
}));
|
||||
}, &st::mainMenuContacts, &st::mainMenuContactsOver);
|
||||
if (Global::PhoneCallsEnabled()) {
|
||||
_menu->addAction(lang(lng_menu_calls), [] {
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<Calls::BoxController>(), [](PeerListBox *box) {
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<Calls::BoxController>(), [](gsl::not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_close), [box] { box->closeBox(); });
|
||||
}));
|
||||
}, &st::mainMenuCalls, &st::mainMenuCallsOver);
|
||||
|
@ -66,6 +66,8 @@
|
||||
<(src_loc)/boxes/notifications_box.h
|
||||
<(src_loc)/boxes/peer_list_box.cpp
|
||||
<(src_loc)/boxes/peer_list_box.h
|
||||
<(src_loc)/boxes/peer_list_controllers.cpp
|
||||
<(src_loc)/boxes/peer_list_controllers.h
|
||||
<(src_loc)/boxes/passcode_box.cpp
|
||||
<(src_loc)/boxes/passcode_box.h
|
||||
<(src_loc)/boxes/photo_crop_box.cpp
|
||||
|
Loading…
Reference in New Issue
Block a user