mirror of https://github.com/procxx/kepka.git
Support auto-migrate to supergroups.
This commit is contained in:
parent
b236844c94
commit
3c44bdb6b7
|
@ -1168,6 +1168,69 @@ void ApiWrap::requestPeer(not_null<PeerData*> peer) {
|
||||||
_peerRequests.insert(peer, requestId);
|
_peerRequests.insert(peer, requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiWrap::migrateChat(
|
||||||
|
not_null<ChatData*> chat,
|
||||||
|
FnMut<void(not_null<ChannelData*>)> done,
|
||||||
|
FnMut<void(const RPCError &)> fail) {
|
||||||
|
const auto callback = [&] {
|
||||||
|
return MigrateCallbacks{ std::move(done), std::move(fail) };
|
||||||
|
};
|
||||||
|
const auto i = _migrateCallbacks.find(chat);
|
||||||
|
if (i != end(_migrateCallbacks)) {
|
||||||
|
i->second.push_back(callback());
|
||||||
|
return;
|
||||||
|
} else if (const auto channel = chat->migrateTo()) {
|
||||||
|
Notify::peerUpdatedDelayed(
|
||||||
|
chat,
|
||||||
|
Notify::PeerUpdate::Flag::MigrationChanged);
|
||||||
|
crl::on_main([=, done = std::move(done)]() mutable {
|
||||||
|
Notify::peerUpdatedSendDelayed();
|
||||||
|
done(channel);
|
||||||
|
});
|
||||||
|
} else if (chat->isDeactivated()) {
|
||||||
|
crl::on_main([fail = std::move(fail)]() mutable {
|
||||||
|
fail(RPCError::Local(
|
||||||
|
"BAD_MIGRATION",
|
||||||
|
"Chat is already deactivated"));
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else if (!chat->amCreator()) {
|
||||||
|
crl::on_main([fail = std::move(fail)]() mutable {
|
||||||
|
fail(RPCError::Local(
|
||||||
|
"BAD_MIGRATION",
|
||||||
|
"Current user is not the creator of that chat"));
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_migrateCallbacks.emplace(chat).first->second.push_back(callback());
|
||||||
|
request(MTPmessages_MigrateChat(
|
||||||
|
chat->inputChat
|
||||||
|
)).done([=](const MTPUpdates &result) {
|
||||||
|
applyUpdates(result);
|
||||||
|
Notify::peerUpdatedSendDelayed();
|
||||||
|
|
||||||
|
const auto channel = chat->migrateTo();
|
||||||
|
if (auto handlers = _migrateCallbacks.take(chat)) {
|
||||||
|
for (auto &handler : *handlers) {
|
||||||
|
if (channel) {
|
||||||
|
handler.done(channel);
|
||||||
|
} else {
|
||||||
|
handler.fail(RPCError::Local(
|
||||||
|
"MIGRATION_FAIL",
|
||||||
|
"No channel"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).fail([=](const RPCError &error) {
|
||||||
|
if (auto handlers = _migrateCallbacks.take(chat)) {
|
||||||
|
for (auto &handler : *handlers) {
|
||||||
|
handler.fail(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
void ApiWrap::markMediaRead(
|
void ApiWrap::markMediaRead(
|
||||||
const base::flat_set<not_null<HistoryItem*>> &items) {
|
const base::flat_set<not_null<HistoryItem*>> &items) {
|
||||||
auto markedIds = QVector<MTPint>();
|
auto markedIds = QVector<MTPint>();
|
||||||
|
|
|
@ -137,6 +137,11 @@ public:
|
||||||
not_null<UserData*> user,
|
not_null<UserData*> user,
|
||||||
const MTPUserFull &result);
|
const MTPUserFull &result);
|
||||||
|
|
||||||
|
void migrateChat(
|
||||||
|
not_null<ChatData*> chat,
|
||||||
|
FnMut<void(not_null<ChannelData*>)> done,
|
||||||
|
FnMut<void(const RPCError &)> fail = nullptr);
|
||||||
|
|
||||||
void markMediaRead(const base::flat_set<not_null<HistoryItem*>> &items);
|
void markMediaRead(const base::flat_set<not_null<HistoryItem*>> &items);
|
||||||
void markMediaRead(not_null<HistoryItem*> item);
|
void markMediaRead(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
|
@ -727,6 +732,14 @@ private:
|
||||||
FnMut<void(const MTPChatInvite &result)> _checkInviteDone;
|
FnMut<void(const MTPChatInvite &result)> _checkInviteDone;
|
||||||
FnMut<void(const RPCError &error)> _checkInviteFail;
|
FnMut<void(const RPCError &error)> _checkInviteFail;
|
||||||
|
|
||||||
|
struct MigrateCallbacks {
|
||||||
|
FnMut<void(not_null<ChannelData*>)> done;
|
||||||
|
FnMut<void(const RPCError&)> fail;
|
||||||
|
};
|
||||||
|
base::flat_map<
|
||||||
|
not_null<ChatData*>,
|
||||||
|
std::vector<MigrateCallbacks>> _migrateCallbacks;
|
||||||
|
|
||||||
std::vector<FnMut<void(const MTPUser &)>> _supportContactCallbacks;
|
std::vector<FnMut<void(const MTPUser &)>> _supportContactCallbacks;
|
||||||
|
|
||||||
base::flat_map<FullMsgId, not_null<PeerData*>> _peerPhotoUploads;
|
base::flat_map<FullMsgId, not_null<PeerData*>> _peerPhotoUploads;
|
||||||
|
|
|
@ -608,8 +608,7 @@ void GroupInfoBox::submit() {
|
||||||
};
|
};
|
||||||
Ui::show(
|
Ui::show(
|
||||||
Box<PeerListBox>(
|
Box<PeerListBox>(
|
||||||
std::make_unique<AddParticipantsBoxController>(
|
std::make_unique<AddParticipantsBoxController>(),
|
||||||
nullptr),
|
|
||||||
std::move(initBox)),
|
std::move(initBox)),
|
||||||
LayerOption::KeepOther);
|
LayerOption::KeepOther);
|
||||||
}
|
}
|
||||||
|
|
|
@ -318,12 +318,15 @@ int PeerListBox::peerListSelectedRowsCount() {
|
||||||
return _select ? _select->entity()->getItemsCount() : 0;
|
return _select ? _select->entity()->getItemsCount() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<not_null<PeerData*>> PeerListBox::peerListCollectSelectedRows() {
|
auto PeerListBox::peerListCollectSelectedRows()
|
||||||
auto result = std::vector<not_null<PeerData*>> {};
|
-> std::vector<not_null<PeerData*>> {
|
||||||
auto items = _select ? _select->entity()->getItems() : QVector<uint64> {};
|
auto result = std::vector<not_null<PeerData*>>();
|
||||||
|
auto items = _select
|
||||||
|
? _select->entity()->getItems()
|
||||||
|
: QVector<uint64>();
|
||||||
if (!items.empty()) {
|
if (!items.empty()) {
|
||||||
result.reserve(items.size());
|
result.reserve(items.size());
|
||||||
for_const (auto itemId, items) {
|
for (const auto itemId : items) {
|
||||||
result.push_back(App::peer(itemId));
|
result.push_back(App::peer(itemId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -303,6 +303,10 @@ public:
|
||||||
std::unique_ptr<SavedStateBase> state) {
|
std::unique_ptr<SavedStateBase> state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::lifetime &lifetime() {
|
||||||
|
return _lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
not_null<PeerListSearchDelegate*> delegate() const {
|
not_null<PeerListSearchDelegate*> delegate() const {
|
||||||
return _delegate;
|
return _delegate;
|
||||||
|
@ -310,6 +314,7 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PeerListSearchDelegate *_delegate = nullptr;
|
PeerListSearchDelegate *_delegate = nullptr;
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -320,7 +325,8 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
// Search works only with RowId == peer->id.
|
// Search works only with RowId == peer->id.
|
||||||
PeerListController(std::unique_ptr<PeerListSearchController> searchController = nullptr);
|
PeerListController(
|
||||||
|
std::unique_ptr<PeerListSearchController> searchController = {});
|
||||||
|
|
||||||
void setDelegate(not_null<PeerListDelegate*> delegate) {
|
void setDelegate(not_null<PeerListDelegate*> delegate) {
|
||||||
_delegate = delegate;
|
_delegate = delegate;
|
||||||
|
|
|
@ -307,7 +307,9 @@ bool ChatsListBoxController::appendRow(not_null<History*> history) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContactsBoxController::ContactsBoxController(std::unique_ptr<PeerListSearchController> searchController) : PeerListController(std::move(searchController)) {
|
ContactsBoxController::ContactsBoxController(
|
||||||
|
std::unique_ptr<PeerListSearchController> searchController)
|
||||||
|
: PeerListController(std::move(searchController)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContactsBoxController::prepare() {
|
void ContactsBoxController::prepare() {
|
||||||
|
|
|
@ -114,9 +114,13 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ContactsBoxController : public PeerListController, protected base::Subscriber {
|
class ContactsBoxController
|
||||||
|
: public PeerListController
|
||||||
|
, protected base::Subscriber {
|
||||||
public:
|
public:
|
||||||
ContactsBoxController(std::unique_ptr<PeerListSearchController> searchController = std::make_unique<PeerListGlobalSearchController>());
|
ContactsBoxController(
|
||||||
|
std::unique_ptr<PeerListSearchController> searchController
|
||||||
|
= std::make_unique<PeerListGlobalSearchController>());
|
||||||
|
|
||||||
void prepare() override final;
|
void prepare() override final;
|
||||||
std::unique_ptr<PeerListRow> createSearchRow(not_null<PeerData*> peer) override final;
|
std::unique_ptr<PeerListRow> createSearchRow(not_null<PeerData*> peer) override final;
|
||||||
|
|
|
@ -40,61 +40,63 @@ base::flat_set<not_null<UserData*>> GetAlreadyInFromPeer(PeerData *peer) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InviteSelectedUsers(
|
|
||||||
not_null<PeerListBox*> box,
|
|
||||||
not_null<PeerData*> chat) {
|
|
||||||
const auto rows = box->peerListCollectSelectedRows();
|
|
||||||
const auto users = ranges::view::all(
|
|
||||||
rows
|
|
||||||
) | ranges::view::transform([](not_null<PeerData*> peer) {
|
|
||||||
Expects(peer->isUser());
|
|
||||||
Expects(!peer->isSelf());
|
|
||||||
|
|
||||||
return not_null<UserData*>(peer->asUser());
|
|
||||||
}) | ranges::to_vector;
|
|
||||||
if (users.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
chat->session().api().addChatParticipants(chat, users);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
AddParticipantsBoxController::AddParticipantsBoxController(PeerData *peer)
|
AddParticipantsBoxController::AddParticipantsBoxController()
|
||||||
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
|
: ContactsBoxController(
|
||||||
, _peer(peer)
|
std::make_unique<PeerListGlobalSearchController>()) {
|
||||||
, _alreadyIn(GetAlreadyInFromPeer(peer)) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AddParticipantsBoxController::AddParticipantsBoxController(
|
AddParticipantsBoxController::AddParticipantsBoxController(
|
||||||
not_null<ChannelData*> channel,
|
not_null<PeerData*> peer)
|
||||||
|
: AddParticipantsBoxController(peer, GetAlreadyInFromPeer(peer)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
AddParticipantsBoxController::AddParticipantsBoxController(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
base::flat_set<not_null<UserData*>> &&alreadyIn)
|
base::flat_set<not_null<UserData*>> &&alreadyIn)
|
||||||
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
|
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
|
||||||
, _peer(channel)
|
, _peer(peer)
|
||||||
, _alreadyIn(std::move(alreadyIn)) {
|
, _alreadyIn(std::move(alreadyIn)) {
|
||||||
|
subscribeToMigration();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddParticipantsBoxController::subscribeToMigration() {
|
||||||
|
Expects(_peer != nullptr);
|
||||||
|
|
||||||
|
SubscribeToMigration(
|
||||||
|
_peer,
|
||||||
|
lifetime(),
|
||||||
|
[=](not_null<ChannelData*> channel) { _peer = channel; });
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddParticipantsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
void AddParticipantsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||||
auto count = fullCount();
|
auto count = fullCount();
|
||||||
auto limit = (_peer && _peer->isMegagroup()) ? Global::MegagroupSizeMax() : Global::ChatSizeMax();
|
auto limit = _peer && (_peer->isChat() || _peer->isMegagroup())
|
||||||
|
? Global::MegagroupSizeMax()
|
||||||
|
: Global::ChatSizeMax();
|
||||||
if (count < limit || row->checked()) {
|
if (count < limit || row->checked()) {
|
||||||
delegate()->peerListSetRowChecked(row, !row->checked());
|
delegate()->peerListSetRowChecked(row, !row->checked());
|
||||||
updateTitle();
|
updateTitle();
|
||||||
} else if (auto channel = _peer ? _peer->asChannel() : nullptr) {
|
} else if (const auto channel = _peer ? _peer->asChannel() : nullptr) {
|
||||||
if (!_peer->isMegagroup()) {
|
if (!_peer->isMegagroup()) {
|
||||||
Ui::show(
|
Ui::show(
|
||||||
Box<MaxInviteBox>(_peer->asChannel()),
|
Box<MaxInviteBox>(_peer->asChannel()),
|
||||||
LayerOption::KeepOther);
|
LayerOption::KeepOther);
|
||||||
}
|
}
|
||||||
} else if (count >= Global::ChatSizeMax() && count < Global::MegagroupSizeMax()) {
|
} else if (count >= Global::ChatSizeMax()
|
||||||
|
&& count < Global::MegagroupSizeMax()) {
|
||||||
|
// #TODO groups new error about "after creating"
|
||||||
Ui::show(
|
Ui::show(
|
||||||
Box<InformBox>(lng_profile_add_more_after_upgrade(lt_count, Global::MegagroupSizeMax())),
|
Box<InformBox>(lng_profile_add_more_after_upgrade(
|
||||||
|
lt_count,
|
||||||
|
Global::MegagroupSizeMax())),
|
||||||
LayerOption::KeepOther);
|
LayerOption::KeepOther);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddParticipantsBoxController::itemDeselectedHook(not_null<PeerData*> peer) {
|
void AddParticipantsBoxController::itemDeselectedHook(
|
||||||
|
not_null<PeerData*> peer) {
|
||||||
updateTitle();
|
updateTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,23 +108,26 @@ int AddParticipantsBoxController::alreadyInCount() const {
|
||||||
if (!_peer) {
|
if (!_peer) {
|
||||||
return 1; // self
|
return 1; // self
|
||||||
}
|
}
|
||||||
if (auto chat = _peer->asChat()) {
|
if (const auto chat = _peer->asChat()) {
|
||||||
return qMax(chat->count, 1);
|
return qMax(chat->count, 1);
|
||||||
} else if (auto channel = _peer->asChannel()) {
|
} else if (const auto channel = _peer->asChannel()) {
|
||||||
return qMax(channel->membersCount(), int(_alreadyIn.size()));
|
return qMax(channel->membersCount(), int(_alreadyIn.size()));
|
||||||
}
|
}
|
||||||
Unexpected("User in AddParticipantsBoxController::alreadyInCount");
|
Unexpected("User in AddParticipantsBoxController::alreadyInCount");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddParticipantsBoxController::isAlreadyIn(not_null<UserData*> user) const {
|
bool AddParticipantsBoxController::isAlreadyIn(
|
||||||
|
not_null<UserData*> user) const {
|
||||||
if (!_peer) {
|
if (!_peer) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (auto chat = _peer->asChat()) {
|
if (const auto chat = _peer->asChat()) {
|
||||||
return chat->participants.contains(user);
|
|
||||||
} else if (auto channel = _peer->asChannel()) {
|
|
||||||
return _alreadyIn.contains(user)
|
return _alreadyIn.contains(user)
|
||||||
|| (channel->isMegagroup() && base::contains(channel->mgInfo->lastParticipants, user));
|
|| chat->participants.contains(user);
|
||||||
|
} else if (const auto channel = _peer->asChannel()) {
|
||||||
|
return _alreadyIn.contains(user)
|
||||||
|
|| (channel->isMegagroup()
|
||||||
|
&& base::contains(channel->mgInfo->lastParticipants, user));
|
||||||
}
|
}
|
||||||
Unexpected("User in AddParticipantsBoxController::isAlreadyIn");
|
Unexpected("User in AddParticipantsBoxController::isAlreadyIn");
|
||||||
}
|
}
|
||||||
|
@ -144,25 +149,49 @@ std::unique_ptr<PeerListRow> AddParticipantsBoxController::createRow(
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddParticipantsBoxController::updateTitle() {
|
void AddParticipantsBoxController::updateTitle() {
|
||||||
auto additional = (_peer && _peer->isChannel() && !_peer->isMegagroup())
|
const auto additional = (_peer
|
||||||
|
&& _peer->isChannel()
|
||||||
|
&& !_peer->isMegagroup())
|
||||||
? QString() :
|
? QString() :
|
||||||
QString("%1 / %2").arg(fullCount()).arg(Global::MegagroupSizeMax());
|
QString("%1 / %2").arg(fullCount()).arg(Global::MegagroupSizeMax());
|
||||||
delegate()->peerListSetTitle(langFactory(lng_profile_add_participant));
|
delegate()->peerListSetTitle(langFactory(lng_profile_add_participant));
|
||||||
delegate()->peerListSetAdditionalTitle([additional] { return additional; });
|
delegate()->peerListSetAdditionalTitle([=] { return additional; });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddParticipantsBoxController::inviteSelectedUsers(
|
||||||
|
not_null<PeerListBox*> box) const {
|
||||||
|
Expects(_peer != nullptr);
|
||||||
|
|
||||||
|
const auto rows = box->peerListCollectSelectedRows();
|
||||||
|
const auto users = ranges::view::all(
|
||||||
|
rows
|
||||||
|
) | ranges::view::transform([](not_null<PeerData*> peer) {
|
||||||
|
Expects(peer->isUser());
|
||||||
|
Expects(!peer->isSelf());
|
||||||
|
|
||||||
|
return not_null<UserData*>(peer->asUser());
|
||||||
|
}) | ranges::to_vector;
|
||||||
|
if (users.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_peer->session().api().addChatParticipants(_peer, users);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddParticipantsBoxController::Start(not_null<ChatData*> chat) {
|
void AddParticipantsBoxController::Start(not_null<ChatData*> chat) {
|
||||||
|
auto controller = std::make_unique<AddParticipantsBoxController>(chat);
|
||||||
|
const auto weak = controller.get();
|
||||||
auto initBox = [=](not_null<PeerListBox*> box) {
|
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||||
box->addButton(langFactory(lng_participant_invite), [=] {
|
box->addButton(langFactory(lng_participant_invite), [=] {
|
||||||
if (InviteSelectedUsers(box, chat)) {
|
if (weak->inviteSelectedUsers(box)) {
|
||||||
Ui::showPeerHistory(chat, ShowAtTheEndMsgId);
|
Ui::showPeerHistory(chat, ShowAtTheEndMsgId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
|
box->addButton(langFactory(lng_cancel), [=] { box->closeBox(); });
|
||||||
};
|
};
|
||||||
Ui::show(
|
Ui::show(
|
||||||
Box<PeerListBox>(
|
Box<PeerListBox>(
|
||||||
std::make_unique<AddParticipantsBoxController>(chat),
|
std::move(controller),
|
||||||
std::move(initBox)),
|
std::move(initBox)),
|
||||||
LayerOption::KeepOther);
|
LayerOption::KeepOther);
|
||||||
}
|
}
|
||||||
|
@ -171,10 +200,13 @@ void AddParticipantsBoxController::Start(
|
||||||
not_null<ChannelData*> channel,
|
not_null<ChannelData*> channel,
|
||||||
base::flat_set<not_null<UserData*>> &&alreadyIn,
|
base::flat_set<not_null<UserData*>> &&alreadyIn,
|
||||||
bool justCreated) {
|
bool justCreated) {
|
||||||
auto initBox = [channel, justCreated](not_null<PeerListBox*> box) {
|
auto controller = std::make_unique<AddParticipantsBoxController>(
|
||||||
auto subscription = std::make_shared<rpl::lifetime>();
|
channel,
|
||||||
box->addButton(langFactory(lng_participant_invite), [=, copy = subscription] {
|
std::move(alreadyIn));
|
||||||
if (InviteSelectedUsers(box, channel)) {
|
const auto weak = controller.get();
|
||||||
|
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||||
|
box->addButton(langFactory(lng_participant_invite), [=] {
|
||||||
|
if (weak->inviteSelectedUsers(box)) {
|
||||||
if (channel->isMegagroup()) {
|
if (channel->isMegagroup()) {
|
||||||
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
|
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
|
||||||
} else {
|
} else {
|
||||||
|
@ -182,18 +214,18 @@ void AddParticipantsBoxController::Start(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
box->addButton(langFactory(justCreated ? lng_create_group_skip : lng_cancel), [box] { box->closeBox(); });
|
box->addButton(
|
||||||
|
langFactory(justCreated ? lng_create_group_skip : lng_cancel),
|
||||||
|
[=] { box->closeBox(); });
|
||||||
if (justCreated) {
|
if (justCreated) {
|
||||||
box->boxClosing() | rpl::start_with_next([=] {
|
box->boxClosing() | rpl::start_with_next([=] {
|
||||||
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
|
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
|
||||||
}, *subscription);
|
}, box->lifetime());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ui::show(
|
Ui::show(
|
||||||
Box<PeerListBox>(
|
Box<PeerListBox>(
|
||||||
std::make_unique<AddParticipantsBoxController>(
|
std::move(controller),
|
||||||
channel,
|
|
||||||
std::move(alreadyIn)),
|
|
||||||
std::move(initBox)),
|
std::move(initBox)),
|
||||||
LayerOption::KeepOther);
|
LayerOption::KeepOther);
|
||||||
}
|
}
|
||||||
|
@ -221,9 +253,23 @@ AddSpecialBoxController::AddSpecialBoxController(
|
||||||
, _additional(peer, Role::Members)
|
, _additional(peer, Role::Members)
|
||||||
, _adminDoneCallback(std::move(adminDoneCallback))
|
, _adminDoneCallback(std::move(adminDoneCallback))
|
||||||
, _bannedDoneCallback(std::move(bannedDoneCallback)) {
|
, _bannedDoneCallback(std::move(bannedDoneCallback)) {
|
||||||
|
subscribeToMigration();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<PeerListRow> AddSpecialBoxController::createSearchRow(not_null<PeerData*> peer) {
|
void AddSpecialBoxController::subscribeToMigration() {
|
||||||
|
SubscribeToMigration(
|
||||||
|
_peer,
|
||||||
|
lifetime(),
|
||||||
|
[=](not_null<ChannelData*> channel) { migrate(channel); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddSpecialBoxController::migrate(not_null<ChannelData*> channel) {
|
||||||
|
_peer = channel;
|
||||||
|
_additional.migrate(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<PeerListRow> AddSpecialBoxController::createSearchRow(
|
||||||
|
not_null<PeerData*> peer) {
|
||||||
if (peer->isSelf()) {
|
if (peer->isSelf()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -237,9 +283,12 @@ void AddSpecialBoxController::prepare() {
|
||||||
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
|
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
|
||||||
const auto title = [&] {
|
const auto title = [&] {
|
||||||
switch (_role) {
|
switch (_role) {
|
||||||
case Role::Admins: return langFactory(lng_channel_add_admin);
|
case Role::Admins:
|
||||||
case Role::Restricted: return langFactory(lng_channel_add_restricted);
|
return langFactory(lng_channel_add_admin);
|
||||||
case Role::Kicked: return langFactory(lng_channel_add_banned);
|
case Role::Restricted:
|
||||||
|
return langFactory(lng_channel_add_restricted);
|
||||||
|
case Role::Kicked:
|
||||||
|
return langFactory(lng_channel_add_banned);
|
||||||
}
|
}
|
||||||
Unexpected("Role in AddSpecialBoxController::prepare()");
|
Unexpected("Role in AddSpecialBoxController::prepare()");
|
||||||
}();
|
}();
|
||||||
|
@ -723,6 +772,14 @@ AddSpecialBoxSearchController::AddSpecialBoxSearchController(
|
||||||
: _peer(peer)
|
: _peer(peer)
|
||||||
, _additional(additional)
|
, _additional(additional)
|
||||||
, _timer([=] { searchOnServer(); }) {
|
, _timer([=] { searchOnServer(); }) {
|
||||||
|
subscribeToMigration();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddSpecialBoxSearchController::subscribeToMigration() {
|
||||||
|
SubscribeToMigration(
|
||||||
|
_peer,
|
||||||
|
lifetime(),
|
||||||
|
[=](not_null<ChannelData*> channel) { _peer = channel; });
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddSpecialBoxSearchController::searchQuery(const QString &query) {
|
void AddSpecialBoxSearchController::searchQuery(const QString &query) {
|
||||||
|
@ -752,10 +809,13 @@ bool AddSpecialBoxSearchController::isLoading() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddSpecialBoxSearchController::searchParticipantsInCache() {
|
bool AddSpecialBoxSearchController::searchParticipantsInCache() {
|
||||||
auto it = _participantsCache.find(_query);
|
const auto i = _participantsCache.find(_query);
|
||||||
if (it != _participantsCache.cend()) {
|
if (i != _participantsCache.cend()) {
|
||||||
_requestId = 0;
|
_requestId = 0;
|
||||||
searchParticipantsDone(_requestId, it->second.result, it->second.requestedCount);
|
searchParticipantsDone(
|
||||||
|
_requestId,
|
||||||
|
i->second.result,
|
||||||
|
i->second.requestedCount);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -837,8 +897,7 @@ void AddSpecialBoxSearchController::searchParticipantsDone(
|
||||||
const auto channel = _peer->asChannel();
|
const auto channel = _peer->asChannel();
|
||||||
auto query = _query;
|
auto query = _query;
|
||||||
if (requestId) {
|
if (requestId) {
|
||||||
auto &session = channel->session();
|
const auto addToCache = [&](auto&&...) {
|
||||||
session.api().parseChannelParticipants(channel, result, [&](auto&&...) {
|
|
||||||
auto it = _participantsQueries.find(requestId);
|
auto it = _participantsQueries.find(requestId);
|
||||||
if (it != _participantsQueries.cend()) {
|
if (it != _participantsQueries.cend()) {
|
||||||
query = it->second.text;
|
query = it->second.text;
|
||||||
|
@ -849,7 +908,11 @@ void AddSpecialBoxSearchController::searchParticipantsDone(
|
||||||
}
|
}
|
||||||
_participantsQueries.erase(it);
|
_participantsQueries.erase(it);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
channel->session().api().parseChannelParticipants(
|
||||||
|
channel,
|
||||||
|
result,
|
||||||
|
addToCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_requestId != requestId) {
|
if (_requestId != requestId) {
|
||||||
|
@ -857,14 +920,14 @@ void AddSpecialBoxSearchController::searchParticipantsDone(
|
||||||
}
|
}
|
||||||
_requestId = 0;
|
_requestId = 0;
|
||||||
result.match([&](const MTPDchannels_channelParticipants &data) {
|
result.match([&](const MTPDchannels_channelParticipants &data) {
|
||||||
auto &list = data.vparticipants.v;
|
const auto &list = data.vparticipants.v;
|
||||||
if (list.size() < requestedCount) {
|
if (list.size() < requestedCount) {
|
||||||
// We want cache to have full information about a query with small
|
// We want cache to have full information about a query with
|
||||||
// results count (if we don't need the second request). So we don't
|
// small results count (that we don't need the second request).
|
||||||
// wait for an empty results list unlike the non-search peer list.
|
// So we don't wait for empty list unlike the non-search case.
|
||||||
_participantsLoaded = true;
|
_participantsLoaded = true;
|
||||||
if (list.empty() && _offset == 0) {
|
if (list.empty() && _offset == 0) {
|
||||||
// No results, so we want to request global search immediately.
|
// No results, request global search immediately.
|
||||||
loadMoreRows();
|
loadMoreRows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -946,7 +1009,6 @@ void AddSpecialBoxSearchController::addChatMembers(
|
||||||
if (chat->participants.empty()) {
|
if (chat->participants.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_participantsLoaded = true;
|
|
||||||
|
|
||||||
const auto wordList = TextUtilities::PrepareSearchWords(_query);
|
const auto wordList = TextUtilities::PrepareSearchWords(_query);
|
||||||
if (wordList.empty()) {
|
if (wordList.empty()) {
|
||||||
|
@ -1024,7 +1086,8 @@ void AddSpecialBoxSearchController::addChatsContacts() {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
const auto dialogsIndex = getSmallestIndex(App::main()->dialogsList());
|
const auto dialogsIndex = getSmallestIndex(App::main()->dialogsList());
|
||||||
const auto contactsIndex = getSmallestIndex(App::main()->contactsNoDialogsList());
|
const auto contactsIndex = getSmallestIndex(
|
||||||
|
App::main()->contactsNoDialogsList());
|
||||||
|
|
||||||
const auto filterAndAppend = [&](const Dialogs::List *list) {
|
const auto filterAndAppend = [&](const Dialogs::List *list) {
|
||||||
if (!list) {
|
if (!list) {
|
||||||
|
|
|
@ -18,9 +18,10 @@ public:
|
||||||
not_null<ChannelData*> channel,
|
not_null<ChannelData*> channel,
|
||||||
base::flat_set<not_null<UserData*>> &&alreadyIn);
|
base::flat_set<not_null<UserData*>> &&alreadyIn);
|
||||||
|
|
||||||
AddParticipantsBoxController(PeerData *peer);
|
AddParticipantsBoxController();
|
||||||
|
AddParticipantsBoxController(not_null<PeerData*> peer);
|
||||||
AddParticipantsBoxController(
|
AddParticipantsBoxController(
|
||||||
not_null<ChannelData*> channel,
|
not_null<PeerData*> peer,
|
||||||
base::flat_set<not_null<UserData*>> &&alreadyIn);
|
base::flat_set<not_null<UserData*>> &&alreadyIn);
|
||||||
|
|
||||||
using ContactsBoxController::ContactsBoxController;
|
using ContactsBoxController::ContactsBoxController;
|
||||||
|
@ -39,11 +40,12 @@ private:
|
||||||
base::flat_set<not_null<UserData*>> &&alreadyIn,
|
base::flat_set<not_null<UserData*>> &&alreadyIn,
|
||||||
bool justCreated);
|
bool justCreated);
|
||||||
|
|
||||||
|
bool inviteSelectedUsers(not_null<PeerListBox*> box) const;
|
||||||
|
void subscribeToMigration();
|
||||||
int alreadyInCount() const;
|
int alreadyInCount() const;
|
||||||
bool isAlreadyIn(not_null<UserData*> user) const;
|
bool isAlreadyIn(not_null<UserData*> user) const;
|
||||||
int fullCount() const;
|
int fullCount() const;
|
||||||
void updateTitle();
|
void updateTitle();
|
||||||
bool inviteSelectedUsers(not_null<PeerData*> chat) const;
|
|
||||||
|
|
||||||
PeerData *_peer = nullptr;
|
PeerData *_peer = nullptr;
|
||||||
base::flat_set<not_null<UserData*>> _alreadyIn;
|
base::flat_set<not_null<UserData*>> _alreadyIn;
|
||||||
|
@ -103,6 +105,9 @@ private:
|
||||||
bool prependRow(not_null<UserData*> user);
|
bool prependRow(not_null<UserData*> user);
|
||||||
std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user) const;
|
std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user) const;
|
||||||
|
|
||||||
|
void subscribeToMigration();
|
||||||
|
void migrate(not_null<ChannelData*> channel);
|
||||||
|
|
||||||
not_null<PeerData*> _peer;
|
not_null<PeerData*> _peer;
|
||||||
Role _role = Role::Admins;
|
Role _role = Role::Admins;
|
||||||
int _offset = 0;
|
int _offset = 0;
|
||||||
|
@ -119,7 +124,8 @@ private:
|
||||||
// Finds chat/channel members, then contacts, then global search results.
|
// Finds chat/channel members, then contacts, then global search results.
|
||||||
class AddSpecialBoxSearchController
|
class AddSpecialBoxSearchController
|
||||||
: public PeerListSearchController
|
: public PeerListSearchController
|
||||||
, private MTP::Sender {
|
, private MTP::Sender
|
||||||
|
, private base::Subscriber {
|
||||||
public:
|
public:
|
||||||
using Role = ParticipantsBoxController::Role;
|
using Role = ParticipantsBoxController::Role;
|
||||||
|
|
||||||
|
@ -156,6 +162,8 @@ private:
|
||||||
void addChatsContacts();
|
void addChatsContacts();
|
||||||
void requestGlobal();
|
void requestGlobal();
|
||||||
|
|
||||||
|
void subscribeToMigration();
|
||||||
|
|
||||||
not_null<PeerData*> _peer;
|
not_null<PeerData*> _peer;
|
||||||
not_null<ParticipantsAdditionalData*> _additional;
|
not_null<ParticipantsAdditionalData*> _additional;
|
||||||
|
|
||||||
|
|
|
@ -142,7 +142,16 @@ Fn<void(
|
||||||
const MTPChatAdminRights &oldRights,
|
const MTPChatAdminRights &oldRights,
|
||||||
const MTPChatAdminRights &newRights) {
|
const MTPChatAdminRights &newRights) {
|
||||||
const auto done = [=] { onDone(newRights); };
|
const auto done = [=] { onDone(newRights); };
|
||||||
if (const auto chat = peer->asChat()) {
|
const auto saveForChannel = [=](not_null<ChannelData*> channel) {
|
||||||
|
SaveChannelAdmin(
|
||||||
|
channel,
|
||||||
|
user,
|
||||||
|
oldRights,
|
||||||
|
newRights,
|
||||||
|
done,
|
||||||
|
onFail);
|
||||||
|
};
|
||||||
|
if (const auto chat = peer->asChatNotMigrated()) {
|
||||||
const auto saveChatAdmin = [&](bool isAdmin) {
|
const auto saveChatAdmin = [&](bool isAdmin) {
|
||||||
SaveChatAdmin(chat, user, isAdmin, done, onFail);
|
SaveChatAdmin(chat, user, isAdmin, done, onFail);
|
||||||
};
|
};
|
||||||
|
@ -155,16 +164,10 @@ Fn<void(
|
||||||
} else if (!flags) {
|
} else if (!flags) {
|
||||||
saveChatAdmin(false);
|
saveChatAdmin(false);
|
||||||
} else {
|
} else {
|
||||||
// #TODO groups autoconv
|
peer->session().api().migrateChat(chat, saveForChannel);
|
||||||
}
|
}
|
||||||
} else if (const auto channel = peer->asChannel()) {
|
} else if (const auto channel = peer->asChannelOrMigrated()) {
|
||||||
SaveChannelAdmin(
|
saveForChannel(channel);
|
||||||
channel,
|
|
||||||
user,
|
|
||||||
oldRights,
|
|
||||||
newRights,
|
|
||||||
done,
|
|
||||||
onFail);
|
|
||||||
} else {
|
} else {
|
||||||
Unexpected("Peer in SaveAdminCallback.");
|
Unexpected("Peer in SaveAdminCallback.");
|
||||||
}
|
}
|
||||||
|
@ -182,17 +185,7 @@ Fn<void(
|
||||||
const MTPChatBannedRights &oldRights,
|
const MTPChatBannedRights &oldRights,
|
||||||
const MTPChatBannedRights &newRights) {
|
const MTPChatBannedRights &newRights) {
|
||||||
const auto done = [=] { onDone(newRights); };
|
const auto done = [=] { onDone(newRights); };
|
||||||
if (const auto chat = peer->asChat()) {
|
const auto saveForChannel = [=](not_null<ChannelData*> channel) {
|
||||||
const auto flags = newRights.match([](
|
|
||||||
const MTPDchatBannedRights &data) {
|
|
||||||
return data.vflags.v;
|
|
||||||
});
|
|
||||||
if (!flags) {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
// #TODO groups autoconv
|
|
||||||
}
|
|
||||||
} else if (const auto channel = peer->asChannel()) {
|
|
||||||
SaveChannelRestriction(
|
SaveChannelRestriction(
|
||||||
channel,
|
channel,
|
||||||
user,
|
user,
|
||||||
|
@ -200,12 +193,51 @@ Fn<void(
|
||||||
newRights,
|
newRights,
|
||||||
done,
|
done,
|
||||||
onFail);
|
onFail);
|
||||||
|
};
|
||||||
|
if (const auto chat = peer->asChatNotMigrated()) {
|
||||||
|
const auto flags = newRights.match([](
|
||||||
|
const MTPDchatBannedRights &data) {
|
||||||
|
return data.vflags.v;
|
||||||
|
});
|
||||||
|
if (!flags) {
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
peer->session().api().migrateChat(chat, saveForChannel);
|
||||||
|
}
|
||||||
|
} else if (const auto channel = peer->asChannelOrMigrated()) {
|
||||||
|
saveForChannel(channel);
|
||||||
} else {
|
} else {
|
||||||
Unexpected("Peer in SaveAdminCallback.");
|
Unexpected("Peer in SaveAdminCallback.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SubscribeToMigration(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
rpl::lifetime &lifetime,
|
||||||
|
Fn<void(not_null<ChannelData*>)> migrate) {
|
||||||
|
if (const auto chat = peer->asChat()) {
|
||||||
|
if (const auto channel = peer->migrateTo()) {
|
||||||
|
migrate(channel);
|
||||||
|
} else if (!chat->isDeactivated()) {
|
||||||
|
const auto alive = lifetime.make_state<base::Subscription>();
|
||||||
|
const auto handler = [=](const Notify::PeerUpdate &update) {
|
||||||
|
if (update.peer == peer) {
|
||||||
|
if (const auto channel = peer->migrateTo()) {
|
||||||
|
const auto onstack = base::duplicate(migrate);
|
||||||
|
*alive = base::Subscription();
|
||||||
|
onstack(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
*alive = Notify::PeerUpdated().add_subscription(
|
||||||
|
Notify::PeerUpdatedHandler(
|
||||||
|
Notify::PeerUpdate::Flag::MigrationChanged,
|
||||||
|
handler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ParticipantsAdditionalData::ParticipantsAdditionalData(
|
ParticipantsAdditionalData::ParticipantsAdditionalData(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
Role role)
|
Role role)
|
||||||
|
@ -214,7 +246,8 @@ ParticipantsAdditionalData::ParticipantsAdditionalData(
|
||||||
fillFromPeer();
|
fillFromPeer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParticipantsAdditionalData::infoLoaded(not_null<UserData*> user) const {
|
bool ParticipantsAdditionalData::infoLoaded(
|
||||||
|
not_null<UserData*> user) const {
|
||||||
return _peer->isChat()
|
return _peer->isChat()
|
||||||
|| (_infoNotLoaded.find(user) == end(_infoNotLoaded));
|
|| (_infoNotLoaded.find(user) == end(_infoNotLoaded));
|
||||||
}
|
}
|
||||||
|
@ -286,7 +319,8 @@ bool ParticipantsAdditionalData::isCreator(not_null<UserData*> user) const {
|
||||||
return (_creator == user);
|
return (_creator == user);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParticipantsAdditionalData::isExternal(not_null<UserData*> user) const {
|
bool ParticipantsAdditionalData::isExternal(
|
||||||
|
not_null<UserData*> user) const {
|
||||||
return _peer->isChat()
|
return _peer->isChat()
|
||||||
? !_members.contains(user)
|
? !_members.contains(user)
|
||||||
: _external.find(user) != end(_external);
|
: _external.find(user) != end(_external);
|
||||||
|
@ -531,6 +565,23 @@ UserData *ParticipantsAdditionalData::applyBanned(
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParticipantsAdditionalData::migrate(not_null<ChannelData*> channel) {
|
||||||
|
_peer = channel;
|
||||||
|
fillFromChannel(channel);
|
||||||
|
|
||||||
|
for (const auto user : _admins) {
|
||||||
|
_adminRights.emplace(
|
||||||
|
user,
|
||||||
|
MTP_chatAdminRights(MTP_flags(ChatData::DefaultAdminRights())));
|
||||||
|
if (channel->amCreator()) {
|
||||||
|
_adminCanEdit.emplace(user);
|
||||||
|
}
|
||||||
|
if (_creator) {
|
||||||
|
_adminPromotedBy.emplace(user, _creator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ParticipantsOnlineSorter::ParticipantsOnlineSorter(
|
ParticipantsOnlineSorter::ParticipantsOnlineSorter(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
not_null<PeerListDelegate*> delegate)
|
not_null<PeerListDelegate*> delegate)
|
||||||
|
@ -608,6 +659,7 @@ ParticipantsBoxController::ParticipantsBoxController(
|
||||||
, _peer(peer)
|
, _peer(peer)
|
||||||
, _role(role)
|
, _role(role)
|
||||||
, _additional(peer, _role) {
|
, _additional(peer, _role) {
|
||||||
|
subscribeToMigration();
|
||||||
if (_role == Role::Profile) {
|
if (_role == Role::Profile) {
|
||||||
setupListChangeViewers();
|
setupListChangeViewers();
|
||||||
}
|
}
|
||||||
|
@ -1183,13 +1235,14 @@ bool ParticipantsBoxController::feedMegagroupLastParticipants() {
|
||||||
}
|
}
|
||||||
const auto info = megagroup->mgInfo.get();
|
const auto info = megagroup->mgInfo.get();
|
||||||
//
|
//
|
||||||
// channelFull and channels_channelParticipants members count is desynced
|
// channelFull and channels_channelParticipants members count desynced
|
||||||
// so we almost always have LastParticipantsCountOutdated that is set
|
// so we almost always have LastParticipantsCountOutdated that is set
|
||||||
// inside setMembersCount() and so we almost never use lastParticipants.
|
// inside setMembersCount() and so we almost never use lastParticipants.
|
||||||
//
|
//
|
||||||
// => disable this check temporarily.
|
// => disable this check temporarily.
|
||||||
//
|
//
|
||||||
//if (info->lastParticipantsStatus != MegagroupInfo::LastParticipantsUpToDate) {
|
//if (info->lastParticipantsStatus
|
||||||
|
// != MegagroupInfo::LastParticipantsUpToDate) {
|
||||||
// _channel->updateFull();
|
// _channel->updateFull();
|
||||||
// return false;
|
// return false;
|
||||||
//}
|
//}
|
||||||
|
@ -1227,7 +1280,8 @@ void ParticipantsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParticipantsBoxController::rowActionClicked(not_null<PeerListRow*> row) {
|
void ParticipantsBoxController::rowActionClicked(
|
||||||
|
not_null<PeerListRow*> row) {
|
||||||
Expects(row->peer()->isUser());
|
Expects(row->peer()->isUser());
|
||||||
|
|
||||||
const auto user = row->peer()->asUser();
|
const auto user = row->peer()->asUser();
|
||||||
|
@ -1599,7 +1653,8 @@ void ParticipantsBoxController::recomputeTypeFor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParticipantsBoxController::refreshCustomStatus(not_null<PeerListRow*> row) const {
|
void ParticipantsBoxController::refreshCustomStatus(
|
||||||
|
not_null<PeerListRow*> row) const {
|
||||||
const auto user = row->peer()->asUser();
|
const auto user = row->peer()->asUser();
|
||||||
if (_role == Role::Admins) {
|
if (_role == Role::Admins) {
|
||||||
if (const auto by = _additional.adminPromotedBy(user)) {
|
if (const auto by = _additional.adminPromotedBy(user)) {
|
||||||
|
@ -1623,6 +1678,18 @@ void ParticipantsBoxController::refreshCustomStatus(not_null<PeerListRow*> row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParticipantsBoxController::subscribeToMigration() {
|
||||||
|
SubscribeToMigration(
|
||||||
|
_peer,
|
||||||
|
lifetime(),
|
||||||
|
[=](not_null<ChannelData*> channel) { migrate(channel); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticipantsBoxController::migrate(not_null<ChannelData*> channel) {
|
||||||
|
_peer = channel;
|
||||||
|
_additional.migrate(channel);
|
||||||
|
}
|
||||||
|
|
||||||
ParticipantsBoxSearchController::ParticipantsBoxSearchController(
|
ParticipantsBoxSearchController::ParticipantsBoxSearchController(
|
||||||
not_null<ChannelData*> channel,
|
not_null<ChannelData*> channel,
|
||||||
Role role,
|
Role role,
|
||||||
|
@ -1686,10 +1753,13 @@ bool ParticipantsBoxSearchController::isLoading() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParticipantsBoxSearchController::searchInCache() {
|
bool ParticipantsBoxSearchController::searchInCache() {
|
||||||
auto it = _cache.find(_query);
|
const auto i = _cache.find(_query);
|
||||||
if (it != _cache.cend()) {
|
if (i != _cache.cend()) {
|
||||||
_requestId = 0;
|
_requestId = 0;
|
||||||
searchDone(_requestId, it->second.result, it->second.requestedCount);
|
searchDone(
|
||||||
|
_requestId,
|
||||||
|
i->second.result,
|
||||||
|
i->second.requestedCount);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -1706,11 +1776,14 @@ bool ParticipantsBoxSearchController::loadMoreRows() {
|
||||||
switch (_role) {
|
switch (_role) {
|
||||||
case Role::Admins: // Search for members, appoint as admin on found.
|
case Role::Admins: // Search for members, appoint as admin on found.
|
||||||
case Role::Profile:
|
case Role::Profile:
|
||||||
case Role::Members: return MTP_channelParticipantsSearch(MTP_string(_query));
|
case Role::Members:
|
||||||
case Role::Restricted: return MTP_channelParticipantsBanned(MTP_string(_query));
|
return MTP_channelParticipantsSearch(MTP_string(_query));
|
||||||
case Role::Kicked: return MTP_channelParticipantsKicked(MTP_string(_query));
|
case Role::Restricted:
|
||||||
|
return MTP_channelParticipantsBanned(MTP_string(_query));
|
||||||
|
case Role::Kicked:
|
||||||
|
return MTP_channelParticipantsKicked(MTP_string(_query));
|
||||||
}
|
}
|
||||||
Unexpected("Role in ParticipantsBoxSearchController::loadMoreRows()");
|
Unexpected("Role in ParticipantsBoxSearchController.");
|
||||||
}();
|
}();
|
||||||
|
|
||||||
// For search we request a lot of rows from the first query.
|
// For search we request a lot of rows from the first query.
|
||||||
|
@ -1725,9 +1798,11 @@ bool ParticipantsBoxSearchController::loadMoreRows() {
|
||||||
MTP_int(_offset),
|
MTP_int(_offset),
|
||||||
MTP_int(perPage),
|
MTP_int(perPage),
|
||||||
MTP_int(participantsHash)
|
MTP_int(participantsHash)
|
||||||
)).done([this, perPage](const MTPchannels_ChannelParticipants &result, mtpRequestId requestId) {
|
)).done([=](
|
||||||
|
const MTPchannels_ChannelParticipants &result,
|
||||||
|
mtpRequestId requestId) {
|
||||||
searchDone(requestId, result, perPage);
|
searchDone(requestId, result, perPage);
|
||||||
}).fail([this](const RPCError &error, mtpRequestId requestId) {
|
}).fail([=](const RPCError &error, mtpRequestId requestId) {
|
||||||
if (_requestId == requestId) {
|
if (_requestId == requestId) {
|
||||||
_requestId = 0;
|
_requestId = 0;
|
||||||
_allLoaded = true;
|
_allLoaded = true;
|
||||||
|
@ -1748,7 +1823,7 @@ void ParticipantsBoxSearchController::searchDone(
|
||||||
int requestedCount) {
|
int requestedCount) {
|
||||||
auto query = _query;
|
auto query = _query;
|
||||||
if (requestId) {
|
if (requestId) {
|
||||||
_channel->session().api().parseChannelParticipants(_channel, result, [&](auto&&...) {
|
const auto addToCache = [&](auto&&...) {
|
||||||
auto it = _queries.find(requestId);
|
auto it = _queries.find(requestId);
|
||||||
if (it != _queries.cend()) {
|
if (it != _queries.cend()) {
|
||||||
query = it->second.text;
|
query = it->second.text;
|
||||||
|
@ -1759,20 +1834,23 @@ void ParticipantsBoxSearchController::searchDone(
|
||||||
}
|
}
|
||||||
_queries.erase(it);
|
_queries.erase(it);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
_channel->session().api().parseChannelParticipants(
|
||||||
|
_channel,
|
||||||
|
result,
|
||||||
|
addToCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_requestId != requestId) {
|
if (_requestId != requestId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestId = 0;
|
_requestId = 0;
|
||||||
result.match([&](const MTPDchannels_channelParticipants &data) {
|
result.match([&](const MTPDchannels_channelParticipants &data) {
|
||||||
auto &list = data.vparticipants.v;
|
const auto &list = data.vparticipants.v;
|
||||||
if (list.size() < requestedCount) {
|
if (list.size() < requestedCount) {
|
||||||
// We want cache to have full information about a query with small
|
// We want cache to have full information about a query with
|
||||||
// results count (if we don't need the second request). So we don't
|
// small results count (that we don't need the second request).
|
||||||
// wait for an empty results list unlike the non-search peer list.
|
// So we don't wait for empty list unlike the non-search case.
|
||||||
_allLoaded = true;
|
_allLoaded = true;
|
||||||
}
|
}
|
||||||
const auto overrideRole = (_role == Role::Admins)
|
const auto overrideRole = (_role == Role::Admins)
|
||||||
|
|
|
@ -36,6 +36,11 @@ Fn<void(
|
||||||
Fn<void(const MTPChatBannedRights &newRights)> onDone,
|
Fn<void(const MTPChatBannedRights &newRights)> onDone,
|
||||||
Fn<void()> onFail);
|
Fn<void()> onFail);
|
||||||
|
|
||||||
|
void SubscribeToMigration(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
rpl::lifetime &lifetime,
|
||||||
|
Fn<void(not_null<ChannelData*>)> migrate);
|
||||||
|
|
||||||
enum class ParticipantsRole {
|
enum class ParticipantsRole {
|
||||||
Profile,
|
Profile,
|
||||||
Members,
|
Members,
|
||||||
|
@ -92,6 +97,8 @@ public:
|
||||||
[[nodiscard]] UserData *adminPromotedBy(not_null<UserData*> user) const;
|
[[nodiscard]] UserData *adminPromotedBy(not_null<UserData*> user) const;
|
||||||
[[nodiscard]] UserData *restrictedBy(not_null<UserData*> user) const;
|
[[nodiscard]] UserData *restrictedBy(not_null<UserData*> user) const;
|
||||||
|
|
||||||
|
void migrate(not_null<ChannelData*> channel);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UserData *applyCreator(const MTPDchannelParticipantCreator &data);
|
UserData *applyCreator(const MTPDchannelParticipantCreator &data);
|
||||||
UserData *applyAdmin(const MTPDchannelParticipantAdmin &data);
|
UserData *applyAdmin(const MTPDchannelParticipantAdmin &data);
|
||||||
|
@ -216,6 +223,9 @@ private:
|
||||||
Type computeType(not_null<UserData*> user) const;
|
Type computeType(not_null<UserData*> user) const;
|
||||||
void recomputeTypeFor(not_null<UserData*> user);
|
void recomputeTypeFor(not_null<UserData*> user);
|
||||||
|
|
||||||
|
void subscribeToMigration();
|
||||||
|
void migrate(not_null<ChannelData*> channel);
|
||||||
|
|
||||||
not_null<Window::Navigation*> _navigation;
|
not_null<Window::Navigation*> _navigation;
|
||||||
not_null<PeerData*> _peer;
|
not_null<PeerData*> _peer;
|
||||||
Role _role = Role::Admins;
|
Role _role = Role::Admins;
|
||||||
|
|
|
@ -1109,8 +1109,17 @@ void Controller::saveUsername() {
|
||||||
if (!_savingData.username || *_savingData.username == username) {
|
if (!_savingData.username || *_savingData.username == username) {
|
||||||
return continueSave();
|
return continueSave();
|
||||||
} else if (!channel) {
|
} else if (!channel) {
|
||||||
// #TODO groups autoconv
|
const auto saveForChannel = [=](not_null<ChannelData*> channel) {
|
||||||
return continueSave();
|
if (_peer->asChannel() == channel) {
|
||||||
|
saveUsername();
|
||||||
|
} else {
|
||||||
|
cancelSave();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_peer->session().api().migrateChat(
|
||||||
|
_peer->asChat(),
|
||||||
|
crl::guard(this, saveForChannel));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
request(MTPchannels_UpdateUsername(
|
request(MTPchannels_UpdateUsername(
|
||||||
|
@ -1122,7 +1131,7 @@ void Controller::saveUsername() {
|
||||||
*_savingData.username);
|
*_savingData.username);
|
||||||
continueSave();
|
continueSave();
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
const auto type = error.type();
|
const auto &type = error.type();
|
||||||
if (type == qstr("USERNAME_NOT_MODIFIED")) {
|
if (type == qstr("USERNAME_NOT_MODIFIED")) {
|
||||||
channel->setName(
|
channel->setName(
|
||||||
TextUtilities::SingleLine(channel->name),
|
TextUtilities::SingleLine(channel->name),
|
||||||
|
@ -1156,7 +1165,7 @@ void Controller::saveTitle() {
|
||||||
continueSave();
|
continueSave();
|
||||||
};
|
};
|
||||||
const auto onFail = [=](const RPCError &error) {
|
const auto onFail = [=](const RPCError &error) {
|
||||||
const auto type = error.type();
|
const auto &type = error.type();
|
||||||
if (type == qstr("CHAT_NOT_MODIFIED")
|
if (type == qstr("CHAT_NOT_MODIFIED")
|
||||||
|| type == qstr("CHAT_TITLE_NOT_MODIFIED")) {
|
|| type == qstr("CHAT_TITLE_NOT_MODIFIED")) {
|
||||||
if (const auto channel = _peer->asChannel()) {
|
if (const auto channel = _peer->asChannel()) {
|
||||||
|
@ -1209,7 +1218,7 @@ void Controller::saveDescription() {
|
||||||
)).done([=](const MTPBool &result) {
|
)).done([=](const MTPBool &result) {
|
||||||
successCallback();
|
successCallback();
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
auto type = error.type();
|
const auto &type = error.type();
|
||||||
if (type == qstr("CHAT_ABOUT_NOT_MODIFIED")) {
|
if (type == qstr("CHAT_ABOUT_NOT_MODIFIED")) {
|
||||||
successCallback();
|
successCallback();
|
||||||
return;
|
return;
|
||||||
|
@ -1226,8 +1235,17 @@ void Controller::saveHistoryVisibility() {
|
||||||
|| *_savingData.hiddenPreHistory == hidden) {
|
|| *_savingData.hiddenPreHistory == hidden) {
|
||||||
return continueSave();
|
return continueSave();
|
||||||
} else if (!channel) {
|
} else if (!channel) {
|
||||||
// #TODO groups autoconv
|
const auto saveForChannel = [=](not_null<ChannelData*> channel) {
|
||||||
return continueSave();
|
if (_peer->asChannel() == channel) {
|
||||||
|
saveHistoryVisibility();
|
||||||
|
} else {
|
||||||
|
cancelSave();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_peer->session().api().migrateChat(
|
||||||
|
_peer->asChat(),
|
||||||
|
crl::guard(this, saveForChannel));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
request(MTPchannels_TogglePreHistoryHidden(
|
request(MTPchannels_TogglePreHistoryHidden(
|
||||||
channel->inputChannel,
|
channel->inputChannel,
|
||||||
|
|
|
@ -489,6 +489,34 @@ const ChannelData *PeerData::asMegagroup() const {
|
||||||
: nullptr;
|
: nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChatData *PeerData::asChatNotMigrated() {
|
||||||
|
if (const auto chat = asChat()) {
|
||||||
|
return chat->migrateTo() ? nullptr : chat;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChatData *PeerData::asChatNotMigrated() const {
|
||||||
|
if (const auto chat = asChat()) {
|
||||||
|
return chat->migrateTo() ? nullptr : chat;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelData *PeerData::asChannelOrMigrated() {
|
||||||
|
if (const auto channel = asChannel()) {
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
return migrateTo();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChannelData *PeerData::asChannelOrMigrated() const {
|
||||||
|
if (const auto channel = asChannel()) {
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
return migrateTo();
|
||||||
|
}
|
||||||
|
|
||||||
ChatData *PeerData::migrateFrom() const {
|
ChatData *PeerData::migrateFrom() const {
|
||||||
if (const auto megagroup = asMegagroup()) {
|
if (const auto megagroup = asMegagroup()) {
|
||||||
return megagroup->amIn()
|
return megagroup->amIn()
|
||||||
|
|
|
@ -110,6 +110,10 @@ public:
|
||||||
[[nodiscard]] const ChannelData *asChannel() const;
|
[[nodiscard]] const ChannelData *asChannel() const;
|
||||||
[[nodiscard]] ChannelData *asMegagroup();
|
[[nodiscard]] ChannelData *asMegagroup();
|
||||||
[[nodiscard]] const ChannelData *asMegagroup() const;
|
[[nodiscard]] const ChannelData *asMegagroup() const;
|
||||||
|
[[nodiscard]] ChatData *asChatNotMigrated();
|
||||||
|
[[nodiscard]] const ChatData *asChatNotMigrated() const;
|
||||||
|
[[nodiscard]] ChannelData *asChannelOrMigrated();
|
||||||
|
[[nodiscard]] const ChannelData *asChannelOrMigrated() const;
|
||||||
|
|
||||||
[[nodiscard]] ChatData *migrateFrom() const;
|
[[nodiscard]] ChatData *migrateFrom() const;
|
||||||
[[nodiscard]] ChannelData *migrateTo() const;
|
[[nodiscard]] ChannelData *migrateTo() const;
|
||||||
|
|
|
@ -28,25 +28,20 @@ void ShowSearchFromBox(
|
||||||
peer,
|
peer,
|
||||||
callback = std::move(callback)
|
callback = std::move(callback)
|
||||||
]() -> std::unique_ptr<PeerListController> {
|
]() -> std::unique_ptr<PeerListController> {
|
||||||
if (peer) {
|
if (peer && (peer->isChat() || peer->isMegagroup())) {
|
||||||
if (auto chat = peer->asChat()) {
|
return std::make_unique<Dialogs::SearchFromController>(
|
||||||
return std::make_unique<Dialogs::ChatSearchFromController>(
|
navigation,
|
||||||
navigation,
|
peer,
|
||||||
chat,
|
std::move(callback));
|
||||||
std::move(callback));
|
|
||||||
} else if (auto group = peer->asMegagroup()) {
|
|
||||||
return std::make_unique<Dialogs::ChannelSearchFromController>(
|
|
||||||
navigation,
|
|
||||||
group,
|
|
||||||
std::move(callback));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
if (auto controller = createController()) {
|
if (auto controller = createController()) {
|
||||||
auto subscription = std::make_shared<rpl::lifetime>();
|
auto subscription = std::make_shared<rpl::lifetime>();
|
||||||
auto box = Ui::show(Box<PeerListBox>(std::move(controller), [subscription](not_null<PeerListBox*> box) {
|
auto box = Ui::show(Box<PeerListBox>(std::move(controller), [subscription](not_null<PeerListBox*> box) {
|
||||||
box->addButton(langFactory(lng_cancel), [box, subscription] { box->closeBox(); });
|
box->addButton(langFactory(lng_cancel), [box, subscription] {
|
||||||
|
box->closeBox();
|
||||||
|
});
|
||||||
}), LayerOption::KeepOther);
|
}), LayerOption::KeepOther);
|
||||||
box->boxClosing() | rpl::start_with_next(
|
box->boxClosing() | rpl::start_with_next(
|
||||||
std::move(closedCallback),
|
std::move(closedCallback),
|
||||||
|
@ -54,106 +49,32 @@ void ShowSearchFromBox(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatSearchFromController::ChatSearchFromController(
|
SearchFromController::SearchFromController(
|
||||||
not_null<Window::Navigation*> navigation,
|
not_null<Window::Navigation*> navigation,
|
||||||
not_null<ChatData*> chat,
|
not_null<PeerData*> peer,
|
||||||
Fn<void(not_null<UserData*>)> callback)
|
|
||||||
: PeerListController()
|
|
||||||
, _chat(chat)
|
|
||||||
, _callback(std::move(callback)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatSearchFromController::prepare() {
|
|
||||||
setSearchNoResultsText(lang(lng_blocked_list_not_found));
|
|
||||||
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
|
|
||||||
delegate()->peerListSetTitle(langFactory(lng_search_messages_from));
|
|
||||||
|
|
||||||
rebuildRows();
|
|
||||||
|
|
||||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::MembersChanged, [this](const Notify::PeerUpdate &update) {
|
|
||||||
if (update.peer == _chat) {
|
|
||||||
rebuildRows();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatSearchFromController::rowClicked(not_null<PeerListRow*> row) {
|
|
||||||
Expects(row->peer()->isUser());
|
|
||||||
|
|
||||||
const auto onstack = _callback;
|
|
||||||
onstack(row->peer()->asUser());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatSearchFromController::rebuildRows() {
|
|
||||||
auto ms = getms();
|
|
||||||
auto wasEmpty = !delegate()->peerListFullRowsCount();
|
|
||||||
|
|
||||||
auto now = unixtime();
|
|
||||||
const auto byOnline = [&](not_null<UserData*> user) {
|
|
||||||
return Data::SortByOnlineValue(user, now);
|
|
||||||
};
|
|
||||||
auto ordered = QMultiMap<TimeId, not_null<UserData*>>();
|
|
||||||
if (_chat->noParticipantInfo()) {
|
|
||||||
Auth().api().requestFullPeer(_chat);
|
|
||||||
} else if (!_chat->participants.empty()) {
|
|
||||||
for (const auto user : _chat->participants) {
|
|
||||||
ordered.insertMulti(byOnline(user), user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for_const (auto user, _chat->lastAuthors) {
|
|
||||||
if (user->isInaccessible()) continue;
|
|
||||||
appendRow(user);
|
|
||||||
if (!ordered.isEmpty()) {
|
|
||||||
ordered.remove(byOnline(user), user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!ordered.isEmpty()) {
|
|
||||||
for (auto i = ordered.cend(), b = ordered.cbegin(); i != b;) {
|
|
||||||
appendRow(*(--i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkForEmptyRows();
|
|
||||||
delegate()->peerListRefreshRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatSearchFromController::checkForEmptyRows() {
|
|
||||||
if (delegate()->peerListFullRowsCount()) {
|
|
||||||
setDescriptionText(QString());
|
|
||||||
} else {
|
|
||||||
setDescriptionText(lang(lng_contacts_loading));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatSearchFromController::appendRow(not_null<UserData*> user) {
|
|
||||||
if (!delegate()->peerListFindRow(user->id)) {
|
|
||||||
delegate()->peerListAppendRow(std::make_unique<PeerListRow>(user));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelSearchFromController::ChannelSearchFromController(
|
|
||||||
not_null<Window::Navigation*> navigation,
|
|
||||||
not_null<ChannelData*> channel,
|
|
||||||
Fn<void(not_null<UserData*>)> callback)
|
Fn<void(not_null<UserData*>)> callback)
|
||||||
: ParticipantsBoxController(
|
: ParticipantsBoxController(
|
||||||
navigation,
|
navigation,
|
||||||
channel,
|
peer,
|
||||||
ParticipantsBoxController::Role::Members)
|
ParticipantsBoxController::Role::Members)
|
||||||
, _callback(std::move(callback)) {
|
, _callback(std::move(callback)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelSearchFromController::prepare() {
|
void SearchFromController::prepare() {
|
||||||
ParticipantsBoxController::prepare();
|
ParticipantsBoxController::prepare();
|
||||||
delegate()->peerListSetTitle(langFactory(lng_search_messages_from));
|
delegate()->peerListSetTitle(langFactory(lng_search_messages_from));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelSearchFromController::rowClicked(not_null<PeerListRow*> row) {
|
void SearchFromController::rowClicked(not_null<PeerListRow*> row) {
|
||||||
Expects(row->peer()->isUser());
|
Expects(row->peer()->isUser());
|
||||||
|
|
||||||
const auto onstack = _callback;
|
if (const auto onstack = base::duplicate(_callback)) {
|
||||||
onstack(row->peer()->asUser());
|
onstack(row->peer()->asUser());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<PeerListRow> ChannelSearchFromController::createRow(not_null<UserData*> user) const {
|
std::unique_ptr<PeerListRow> SearchFromController::createRow(
|
||||||
|
not_null<UserData*> user) const {
|
||||||
return std::make_unique<PeerListRow>(user);
|
return std::make_unique<PeerListRow>(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,31 +18,11 @@ void ShowSearchFromBox(
|
||||||
Fn<void(not_null<UserData*>)> callback,
|
Fn<void(not_null<UserData*>)> callback,
|
||||||
Fn<void()> closedCallback);
|
Fn<void()> closedCallback);
|
||||||
|
|
||||||
class ChatSearchFromController : public PeerListController, protected base::Subscriber {
|
class SearchFromController : public ParticipantsBoxController {
|
||||||
public:
|
public:
|
||||||
ChatSearchFromController(
|
SearchFromController(
|
||||||
not_null<Window::Navigation*> navigation,
|
not_null<Window::Navigation*> navigation,
|
||||||
not_null<ChatData*> chat,
|
not_null<PeerData*> peer,
|
||||||
Fn<void(not_null<UserData*>)> callback);
|
|
||||||
|
|
||||||
void prepare() override;
|
|
||||||
void rowClicked(not_null<PeerListRow*> row) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void rebuildRows();
|
|
||||||
void checkForEmptyRows();
|
|
||||||
void appendRow(not_null<UserData*> user);
|
|
||||||
|
|
||||||
not_null<ChatData*> _chat;
|
|
||||||
Fn<void(not_null<UserData*>)> _callback;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class ChannelSearchFromController : public ParticipantsBoxController {
|
|
||||||
public:
|
|
||||||
ChannelSearchFromController(
|
|
||||||
not_null<Window::Navigation*> navigation,
|
|
||||||
not_null<ChannelData*> channel,
|
|
||||||
Fn<void(not_null<UserData*>)> callback);
|
Fn<void(not_null<UserData*>)> callback);
|
||||||
|
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
|
|
|
@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "mtproto/mtp_instance.h"
|
#include "mtproto/mtp_instance.h"
|
||||||
#include "mtproto/rpc_sender.h"
|
#include "mtproto/rpc_sender.h"
|
||||||
#include "mtproto/session.h"
|
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
|
|
||||||
|
@ -180,11 +179,13 @@ void ConcurrentSender::senderRequestDone(
|
||||||
bytes::const_span result) {
|
bytes::const_span result) {
|
||||||
if (auto handlers = _requests.take(requestId)) {
|
if (auto handlers = _requests.take(requestId)) {
|
||||||
try {
|
try {
|
||||||
std::move(handlers->done)(requestId, result);
|
handlers->done(requestId, result);
|
||||||
} catch (Exception &e) {
|
} catch (Exception &e) {
|
||||||
std::move(handlers->fail)(requestId, internal::rpcClientError(
|
handlers->fail(
|
||||||
"RESPONSE_PARSE_FAILED",
|
requestId,
|
||||||
QString("exception text: ") + e.what()));
|
RPCError::Local(
|
||||||
|
"RESPONSE_PARSE_FAILED",
|
||||||
|
QString("exception text: ") + e.what()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,7 +194,7 @@ void ConcurrentSender::senderRequestFail(
|
||||||
mtpRequestId requestId,
|
mtpRequestId requestId,
|
||||||
RPCError &&error) {
|
RPCError &&error) {
|
||||||
if (auto handlers = _requests.take(requestId)) {
|
if (auto handlers = _requests.take(requestId)) {
|
||||||
std::move(handlers->fail)(requestId, std::move(error));
|
handlers->fail(requestId, std::move(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "mtproto/dedicated_file_loader.h"
|
#include "mtproto/dedicated_file_loader.h"
|
||||||
|
|
||||||
#include "mtproto/session.h"
|
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "messenger.h"
|
#include "messenger.h"
|
||||||
|
|
||||||
|
@ -110,7 +109,9 @@ void WeakInstance::die() {
|
||||||
if (instance) {
|
if (instance) {
|
||||||
instance->cancel(requestId);
|
instance->cancel(requestId);
|
||||||
}
|
}
|
||||||
fail(MTP::internal::rpcClientError("UNAVAILABLE"));
|
fail(RPCError::Local(
|
||||||
|
"UNAVAILABLE",
|
||||||
|
"MTP instance is not available."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +126,9 @@ bool WeakInstance::removeRequest(mtpRequestId requestId) {
|
||||||
void WeakInstance::reportUnavailable(
|
void WeakInstance::reportUnavailable(
|
||||||
Fn<void(const RPCError &error)> callback) {
|
Fn<void(const RPCError &error)> callback) {
|
||||||
InvokeQueued(this, [=] {
|
InvokeQueued(this, [=] {
|
||||||
callback(MTP::internal::rpcClientError("UNAVAILABLE"));
|
callback(RPCError::Local(
|
||||||
|
"UNAVAILABLE",
|
||||||
|
"MTP instance is not available."));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -956,7 +956,14 @@ void Instance::Private::clearCallbacks(mtpRequestId requestId, int32 errorCode)
|
||||||
"Request: %1, error code: %2"
|
"Request: %1, error code: %2"
|
||||||
).arg(requestId
|
).arg(requestId
|
||||||
).arg(errorCode));
|
).arg(errorCode));
|
||||||
rpcErrorOccured(requestId, h, internal::rpcClientError("CLEAR_CALLBACK", QString("did not handle request %1, error code %2").arg(requestId).arg(errorCode)));
|
rpcErrorOccured(
|
||||||
|
requestId,
|
||||||
|
h,
|
||||||
|
RPCError::Local(
|
||||||
|
"CLEAR_CALLBACK",
|
||||||
|
QString("did not handle request %1, error code %2"
|
||||||
|
).arg(requestId
|
||||||
|
).arg(errorCode)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1017,14 +1024,13 @@ void Instance::Private::execCallback(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (h.onDone || h.onFail) {
|
if (h.onDone || h.onFail) {
|
||||||
const auto handleError = [&](const MTPRpcError &error) {
|
const auto handleError = [&](const RPCError &error) {
|
||||||
const auto wrapped = RPCError(error);
|
|
||||||
DEBUG_LOG(("RPC Info: "
|
DEBUG_LOG(("RPC Info: "
|
||||||
"error received, code %1, type %2, description: %3"
|
"error received, code %1, type %2, description: %3"
|
||||||
).arg(wrapped.code()
|
).arg(error.code()
|
||||||
).arg(wrapped.type()
|
).arg(error.type()
|
||||||
).arg(wrapped.description()));
|
).arg(error.description()));
|
||||||
if (rpcErrorOccured(requestId, h, wrapped)) {
|
if (rpcErrorOccured(requestId, h, error)) {
|
||||||
unregisterRequest(requestId);
|
unregisterRequest(requestId);
|
||||||
} else {
|
} else {
|
||||||
QMutexLocker locker(&_parserMapLock);
|
QMutexLocker locker(&_parserMapLock);
|
||||||
|
@ -1045,7 +1051,7 @@ void Instance::Private::execCallback(
|
||||||
unregisterRequest(requestId);
|
unregisterRequest(requestId);
|
||||||
}
|
}
|
||||||
} catch (Exception &e) {
|
} catch (Exception &e) {
|
||||||
handleError(internal::rpcClientError(
|
handleError(RPCError::Local(
|
||||||
"RESPONSE_PARSE_FAILED",
|
"RESPONSE_PARSE_FAILED",
|
||||||
QString("exception text: ") + e.what()));
|
QString("exception text: ") + e.what()));
|
||||||
}
|
}
|
||||||
|
@ -1103,7 +1109,10 @@ void Instance::Private::importDone(const MTPauth_Authorization &result, mtpReque
|
||||||
//
|
//
|
||||||
// Don't log out on export/import problems, perhaps this is a server side error.
|
// Don't log out on export/import problems, perhaps this is a server side error.
|
||||||
//
|
//
|
||||||
//RPCError error(internal::rpcClientError("AUTH_IMPORT_FAIL", QString("did not find import request in requestsByDC, request %1").arg(requestId)));
|
//const auto error = RPCError::Local(
|
||||||
|
// "AUTH_IMPORT_FAIL",
|
||||||
|
// QString("did not find import request in requestsByDC, "
|
||||||
|
// "request %1").arg(requestId));
|
||||||
//if (_globalHandler.onFail && hasAuthorization()) {
|
//if (_globalHandler.onFail && hasAuthorization()) {
|
||||||
// (*_globalHandler.onFail)(requestId, error); // auth failed in main dc
|
// (*_globalHandler.onFail)(requestId, error); // auth failed in main dc
|
||||||
//}
|
//}
|
||||||
|
@ -1156,7 +1165,10 @@ void Instance::Private::exportDone(const MTPauth_ExportedAuthorization &result,
|
||||||
//
|
//
|
||||||
// Don't log out on export/import problems, perhaps this is a server side error.
|
// Don't log out on export/import problems, perhaps this is a server side error.
|
||||||
//
|
//
|
||||||
//RPCError error(internal::rpcClientError("AUTH_IMPORT_FAIL", QString("did not find target dcWithShift, request %1").arg(requestId)));
|
//const auto error = RPCError::Local(
|
||||||
|
// "AUTH_IMPORT_FAIL",
|
||||||
|
// QString("did not find target dcWithShift, request %1"
|
||||||
|
// ).arg(requestId));
|
||||||
//if (_globalHandler.onFail && hasAuthorization()) {
|
//if (_globalHandler.onFail && hasAuthorization()) {
|
||||||
// (*_globalHandler.onFail)(requestId, error); // auth failed in main dc
|
// (*_globalHandler.onFail)(requestId, error); // auth failed in main dc
|
||||||
//}
|
//}
|
||||||
|
|
|
@ -32,10 +32,21 @@ public:
|
||||||
TimeoutError
|
TimeoutError
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
static RPCError Local(const QString &type, const QString &description) {
|
||||||
|
return MTP_rpc_error(
|
||||||
|
MTP_int(0),
|
||||||
|
MTP_bytes(
|
||||||
|
("CLIENT_"
|
||||||
|
+ type
|
||||||
|
+ (description.length()
|
||||||
|
? (": " + description)
|
||||||
|
: QString())).toUtf8()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
int32 _code;
|
int32 _code;
|
||||||
QString _type, _description;
|
QString _type, _description;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
|
|
|
@ -156,8 +156,11 @@ void Session::createDcData() {
|
||||||
connect(dc.get(), SIGNAL(connectionWasInited()), this, SLOT(connectionWasInitedForDC()), Qt::QueuedConnection);
|
connect(dc.get(), SIGNAL(connectionWasInited()), this, SLOT(connectionWasInitedForDC()), Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Session::rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err) { // return true if need to clean request data
|
bool Session::rpcErrorOccured(
|
||||||
return _instance->rpcErrorOccured(requestId, onFail, err);
|
mtpRequestId requestId,
|
||||||
|
const RPCFailHandlerPtr &onFail,
|
||||||
|
const RPCError &error) { // return true if need to clean request data
|
||||||
|
return _instance->rpcErrorOccured(requestId, onFail, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::restart() {
|
void Session::restart() {
|
||||||
|
@ -618,9 +621,5 @@ Session::~Session() {
|
||||||
Assert(_connection == nullptr);
|
Assert(_connection == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
MTPrpcError rpcClientError(const QString &type, const QString &description) {
|
|
||||||
return MTP_rpc_error(MTP_int(0), MTP_string(("CLIENT_" + type + (description.length() ? (": " + description) : "")).toUtf8().constData()));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace MTP
|
} // namespace MTP
|
||||||
|
|
|
@ -402,7 +402,5 @@ inline not_null<QReadWriteLock*> SessionData::keyMutex() const {
|
||||||
return _owner->keyMutex();
|
return _owner->keyMutex();
|
||||||
}
|
}
|
||||||
|
|
||||||
MTPrpcError rpcClientError(const QString &type, const QString &description = QString());
|
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace MTP
|
} // namespace MTP
|
||||||
|
|
|
@ -107,11 +107,7 @@ History *FindWastedPin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddChatMembers(not_null<ChatData*> chat) {
|
void AddChatMembers(not_null<ChatData*> chat) {
|
||||||
if (chat->count >= Global::ChatSizeMax() && chat->amCreator()) {
|
AddParticipantsBoxController::Start(chat);
|
||||||
// #TODO convert and add inside AddParticipantsBoxController?
|
|
||||||
} else {
|
|
||||||
AddParticipantsBoxController::Start(chat);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PinnedLimitReached(Dialogs::Key key) {
|
bool PinnedLimitReached(Dialogs::Key key) {
|
||||||
|
|
Loading…
Reference in New Issue