Improve update handling for legacy chats.

This commit is contained in:
John Preston 2019-01-13 12:03:34 +04:00
parent 215856adc3
commit 67d12fa6d2
28 changed files with 650 additions and 797 deletions

View File

@ -395,7 +395,7 @@ void ApiWrap::importChatInvite(const QString &hash) {
"(MainWidget::inviteImportDone)").arg(result.type()));
});
}).fail([=](const RPCError &error) {
const auto type = error.type();
const auto &type = error.type();
if (type == qstr("CHANNELS_TOO_MUCH")) {
Ui::show(Box<InformBox>(lang(lng_join_channel_error)));
} else if (error.code() == 400) {
@ -416,7 +416,7 @@ void ApiWrap::savePinnedOrder() {
const auto &order = _session->data().pinnedDialogsOrder();
auto peers = QVector<MTPInputDialogPeer>();
peers.reserve(order.size());
for (const auto pinned : base::reversed(order)) {
for (const auto &pinned : base::reversed(order)) {
if (const auto history = pinned.history()) {
peers.push_back(MTP_inputDialogPeer(history->peer->input));
} else if (const auto feed = pinned.feed()) {
@ -510,7 +510,9 @@ ApiWrap::MessageDataRequests *ApiWrap::messageDataRequests(ChannelData *channel,
if (channel) {
auto i = _channelMessageDataRequests.find(channel);
if (i == _channelMessageDataRequests.cend()) {
if (onlyExisting) return 0;
if (onlyExisting) {
return nullptr;
}
i = _channelMessageDataRequests.insert(channel, MessageDataRequests());
}
return &i.value();
@ -694,7 +696,7 @@ void ApiWrap::requestDialogEntry(
}
const auto finalize = [=] {
if (const auto callbacks = _dialogRequests.take(history)) {
for (const auto callback : *callbacks) {
for (const auto &callback : *callbacks) {
callback();
}
}
@ -731,7 +733,7 @@ void ApiWrap::requestDialogEntries(
const auto finalize = [=](std::vector<not_null<History*>> histories) {
for (const auto history : histories) {
if (const auto callbacks = _dialogRequests.take(history)) {
for (const auto callback : *callbacks) {
for (const auto &callback : *callbacks) {
callback();
}
}
@ -846,7 +848,7 @@ void ApiWrap::applyFeedDialogs(
if (peerIsChannel(peerId)) {
const auto history = App::history(peerId);
history->applyDialog(dialog.c_dialog());
channels.push_back(history->peer->asChannel());
channels.emplace_back(history->peer->asChannel());
} else {
LOG(("API Error: "
"Unexpected non-channel in feed dialogs list."));
@ -932,18 +934,8 @@ void ApiWrap::gotChatFull(
not_null<PeerData*> peer,
const MTPmessages_ChatFull &result,
mtpRequestId req) {
auto &d = result.c_messages_chatFull();
auto &vc = d.vchats.v;
auto badVersion = false;
if (const auto chat = peer->asChat()) {
badVersion = !vc.isEmpty()
&& (vc[0].type() == mtpc_chat)
&& (vc[0].c_chat().vversion.v < chat->version);
} else if (const auto channel = peer->asChannel()) {
badVersion = !vc.isEmpty()
&& (vc[0].type() == mtpc_channel)
&& (vc[0].c_channel().vversion.v < channel->version);
}
const auto &d = result.c_messages_chatFull();
_session->data().applyMaximumChatVersions(d.vchats);
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
@ -956,7 +948,7 @@ void ApiWrap::gotChatFull(
return;
}
auto &f = d.vfull_chat.c_chatFull();
App::feedParticipants(f.vparticipants, false);
Data::ApplyChatParticipants(chat, f.vparticipants);
if (f.has_bot_info()) {
for (const auto &item : f.vbot_info.v) {
item.match([&](const MTPDbotInfo &data) {
@ -1087,14 +1079,6 @@ void ApiWrap::gotChatFull(
_fullPeerRequests.erase(i);
}
}
if (badVersion) {
if (const auto chat = peer->asChat()) {
chat->version = vc[0].c_chat().vversion.v;
} else if (const auto channel = peer->asChannel()) {
channel->version = vc[0].c_channel().vversion.v;
}
requestPeer(peer);
}
fullPeerUpdated().notify(peer);
}
@ -1157,31 +1141,11 @@ void ApiWrap::requestPeer(not_null<PeerData*> peer) {
};
const auto chatHandler = [=](const MTPmessages_Chats &result) {
_peerRequests.remove(peer);
if (const auto chats = Api::getChatsFromMessagesChats(result)) {
auto &v = chats->v;
bool badVersion = false;
if (const auto chat = peer->asChat()) {
badVersion = !v.isEmpty()
&& (v[0].type() == mtpc_chat)
&& (v[0].c_chat().vversion.v < chat->version);
} else if (const auto channel = peer->asChannel()) {
badVersion = !v.isEmpty()
&& (v[0].type() == mtpc_channel)
&& (v[0].c_channel().vversion.v < channel->version);
}
const auto chat = App::feedChats(*chats);
if (chat == peer) {
if (badVersion) {
if (const auto chat = peer->asChat()) {
chat->version = v[0].c_chat().vversion.v;
} else if (const auto channel = peer->asChannel()) {
channel->version = v[0].c_channel().vversion.v;
}
requestPeer(peer);
}
}
}
const auto &chats = result.match([](const auto &data) {
return data.vchats;
});
_session->data().applyMaximumChatVersions(chats);
App::feedChats(chats);
};
if (const auto user = peer->asUser()) {
return request(MTPusers_GetUsers(
@ -1272,36 +1236,47 @@ void ApiWrap::requestPeers(const QList<PeerData*> &peers) {
chats.reserve(peers.size());
channels.reserve(peers.size());
users.reserve(peers.size());
for (QList<PeerData*>::const_iterator i = peers.cbegin(), e = peers.cend(); i != e; ++i) {
if (!*i || _fullPeerRequests.contains(*i) || _peerRequests.contains(*i)) continue;
if ((*i)->isUser()) {
users.push_back((*i)->asUser()->inputUser);
} else if ((*i)->isChat()) {
chats.push_back((*i)->asChat()->inputChat);
} else if ((*i)->isChannel()) {
channels.push_back((*i)->asChannel()->inputChannel);
for (const auto peer : peers) {
if (!peer
|| _fullPeerRequests.contains(peer)
|| _peerRequests.contains(peer)) {
continue;
}
if (const auto user = peer->asUser()) {
users.push_back(user->inputUser);
} else if (const auto chat = peer->asChat()) {
chats.push_back(chat->inputChat);
} else if (const auto channel = peer->asChannel()) {
channels.push_back(channel->inputChannel);
}
}
auto handleChats = [=](const MTPmessages_Chats &result) {
if (auto chats = Api::getChatsFromMessagesChats(result)) {
App::feedChats(*chats);
}
const auto handleChats = [=](const MTPmessages_Chats &result) {
App::feedChats(result.match([](const auto &data) {
return data.vchats;
}));
};
if (!chats.isEmpty()) {
request(MTPmessages_GetChats(MTP_vector<MTPint>(chats))).done(handleChats).send();
request(MTPmessages_GetChats(
MTP_vector<MTPint>(chats)
)).done(handleChats).send();
}
if (!channels.isEmpty()) {
request(MTPchannels_GetChannels(MTP_vector<MTPInputChannel>(channels))).done(handleChats).send();
request(MTPchannels_GetChannels(
MTP_vector<MTPInputChannel>(channels)
)).done(handleChats).send();
}
if (!users.isEmpty()) {
request(MTPusers_GetUsers(MTP_vector<MTPInputUser>(users))).done([=](const MTPVector<MTPUser> &result) {
request(MTPusers_GetUsers(
MTP_vector<MTPInputUser>(users)
)).done([=](const MTPVector<MTPUser> &result) {
App::feedUsers(result);
}).send();
}
}
void ApiWrap::requestLastParticipants(not_null<ChannelData*> channel) {
if (!channel->isMegagroup() || _participantsRequests.contains(channel)) {
if (!channel->isMegagroup()
|| _participantsRequests.contains(channel)) {
return;
}
@ -1313,7 +1288,7 @@ void ApiWrap::requestLastParticipants(not_null<ChannelData*> channel) {
MTP_int(offset),
MTP_int(Global::ChatSizeMax()),
MTP_int(participantsHash)
)).done([this, channel](const MTPchannels_ChannelParticipants &result) {
)).done([=](const MTPchannels_ChannelParticipants &result) {
_participantsRequests.remove(channel);
parseChannelParticipants(channel, result, [&](
int availableCount,
@ -4357,7 +4332,7 @@ void ApiWrap::sendFiles(
_sendingAlbums.emplace(album->groupId, album);
album->items.reserve(tasks.size());
for (const auto &task : tasks) {
album->items.push_back(SendingAlbum::Item(task->id()));
album->items.emplace_back(task->id());
}
}
_fileLoader->addTasks(std::move(tasks));

View File

@ -47,14 +47,6 @@ struct CloudPasswordState;
namespace Api {
inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Chats &chats) {
switch (chats.type()) {
case mtpc_messages_chats: return &chats.c_messages_chats().vchats;
case mtpc_messages_chatsSlice: return &chats.c_messages_chatsSlice().vchats;
}
return nullptr;
}
template <typename IntRange>
inline int32 CountHash(IntRange &&range) {
uint32 acc = 0;

View File

@ -146,224 +146,6 @@ namespace App {
return Auth().data().processChats(chats);
}
void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos) {
ChatData *chat = 0;
switch (p.type()) {
case mtpc_chatParticipantsForbidden: {
const auto &d(p.c_chatParticipantsForbidden());
chat = App::chat(d.vchat_id.v);
chat->count = -1;
chat->invalidateParticipants();
} break;
case mtpc_chatParticipants: {
const auto &d(p.c_chatParticipants());
chat = App::chat(d.vchat_id.v);
if (!requestBotInfos || chat->version <= d.vversion.v) { // !requestBotInfos is true on getFullChat result
chat->version = d.vversion.v;
auto &v = d.vparticipants.v;
chat->count = v.size();
int32 pversion = chat->participants.empty()
? 1
: (chat->participants.begin()->second + 1);
chat->invitedByMe.clear();
chat->admins.clear();
// #TODO groups
//chat->removeFlags(MTPDchat::Flag::f_admin);
for (auto i = v.cbegin(), e = v.cend(); i != e; ++i) {
int32 uid = 0, inviter = 0;
switch (i->type()) {
case mtpc_chatParticipantCreator: {
const auto &p(i->c_chatParticipantCreator());
uid = p.vuser_id.v;
chat->creator = uid;
} break;
case mtpc_chatParticipantAdmin: {
const auto &p(i->c_chatParticipantAdmin());
uid = p.vuser_id.v;
inviter = p.vinviter_id.v;
} break;
case mtpc_chatParticipant: {
const auto &p(i->c_chatParticipant());
uid = p.vuser_id.v;
inviter = p.vinviter_id.v;
} break;
}
if (!uid) continue;
UserData *user = App::userLoaded(uid);
if (user) {
chat->participants[user] = pversion;
if (inviter == Auth().userId()) {
chat->invitedByMe.insert(user);
}
if (i->type() == mtpc_chatParticipantAdmin) {
chat->admins.insert(user);
if (user->isSelf()) {
// #TODO groups
// chat->addFlags(MTPDchat::Flag::f_admin);
}
}
} else {
chat->invalidateParticipants();
break;
}
}
if (!chat->participants.empty()) {
auto h = App::historyLoaded(chat->id);
bool found = !h || !h->lastKeyboardFrom;
auto botStatus = -1;
for (auto i = chat->participants.begin(); i != chat->participants.end();) {
const auto [user, version] = *i;
if (version < pversion) {
i = chat->participants.erase(i);
} else {
if (user->botInfo) {
botStatus = 2;// (botStatus > 0/* || !user->botInfo->readsAllHistory*/) ? 2 : 1;
if (requestBotInfos && !user->botInfo->inited) {
Auth().api().requestFullPeer(user);
}
}
if (!found && user->id == h->lastKeyboardFrom) {
found = true;
}
++i;
}
}
chat->botStatus = botStatus;
if (!found) {
h->clearLastKeyboard();
}
}
}
} break;
}
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::MembersChanged | Notify::PeerUpdate::Flag::AdminsChanged);
}
void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d) {
ChatData *chat = App::chat(d.vchat_id.v);
if (chat->version + 1 < d.vversion.v) {
chat->version = d.vversion.v;
chat->invalidateParticipants();
Auth().api().requestPeer(chat);
} else if (chat->version <= d.vversion.v && chat->count >= 0) {
chat->version = d.vversion.v;
UserData *user = App::userLoaded(d.vuser_id.v);
if (user) {
if (chat->participants.empty() && chat->count) {
chat->count++;
chat->botStatus = 0;
} else if (chat->participants.find(user) == chat->participants.end()) {
chat->participants[user] = (chat->participants.empty() ? 1 : chat->participants.begin()->second);
if (d.vinviter_id.v == Auth().userId()) {
chat->invitedByMe.insert(user);
} else {
chat->invitedByMe.remove(user);
}
chat->count++;
if (user->botInfo) {
chat->botStatus = 2;// (chat->botStatus > 0/* || !user->botInfo->readsAllHistory*/) ? 2 : 1;
if (!user->botInfo->inited) {
Auth().api().requestFullPeer(user);
}
}
}
} else {
chat->invalidateParticipants();
chat->count++;
}
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::MembersChanged);
}
}
void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d) {
ChatData *chat = App::chat(d.vchat_id.v);
if (chat->version + 1 < d.vversion.v) {
chat->version = d.vversion.v;
chat->invalidateParticipants();
Auth().api().requestPeer(chat);
} else if (chat->version <= d.vversion.v && chat->count > 0) {
chat->version = d.vversion.v;
const auto user = App::userLoaded(d.vuser_id.v);
if (user) {
if (chat->participants.empty()) {
if (chat->count > 0) {
chat->count--;
}
} else {
auto i = chat->participants.find(user);
if (i != chat->participants.end()) {
chat->participants.erase(i);
chat->count--;
chat->invitedByMe.remove(user);
chat->admins.remove(user);
if (user->isSelf()) {
// #TODO groups
// chat->removeFlags(MTPDchat::Flag::f_admin);
}
History *h = App::historyLoaded(chat->id);
if (h && h->lastKeyboardFrom == user->id) {
h->clearLastKeyboard();
}
}
if (chat->botStatus > 0 && user->botInfo) {
int32 botStatus = -1;
for (const auto [participant, v] : chat->participants) {
if (participant->botInfo) {
if (true || botStatus > 0/* || !participant->botInfo->readsAllHistory*/) {
botStatus = 2;
break;
}
botStatus = 1;
}
}
chat->botStatus = botStatus;
}
}
} else {
chat->invalidateParticipants();
chat->count--;
}
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::MembersChanged);
}
}
void feedParticipantAdmin(const MTPDupdateChatParticipantAdmin &d) {
const auto chat = App::chat(d.vchat_id.v);
if (chat->version + 1 < d.vversion.v) {
chat->version = d.vversion.v;
chat->invalidateParticipants();
Auth().api().requestPeer(chat);
} else if (chat->version <= d.vversion.v && chat->count > 0) {
chat->version = d.vversion.v;
const auto user = App::userLoaded(d.vuser_id.v);
if (user) {
if (mtpIsTrue(d.vis_admin)) {
if (user->isSelf()) {
// #TODO groups
// chat->addFlags(MTPDchat::Flag::f_admin);
}
if (chat->noParticipantInfo()) {
Auth().api().requestFullPeer(chat);
} else {
chat->admins.insert(user);
}
} else {
if (user->isSelf()) {
// #TODO groups
//chat->removeFlags(MTPDchat::Flag::f_admin);
}
chat->admins.remove(user);
}
} else {
chat->invalidateParticipants();
}
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::AdminsChanged);
}
}
bool checkEntitiesAndViewsUpdate(const MTPDmessage &m) {
auto peerId = peerFromMTP(m.vto_id);
if (m.has_from_id() && peerId == Auth().userPeerId()) {

View File

@ -70,10 +70,6 @@ namespace App {
PeerData *feedChat(const MTPChat &chat);
PeerData *feedChats(const MTPVector<MTPChat> &chats); // returns last chat
void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos);
void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d);
void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d);
void feedParticipantAdmin(const MTPDupdateChatParticipantAdmin &d);
bool checkEntitiesAndViewsUpdate(const MTPDmessage &m); // returns true if item found and it is not detached
void updateEditedMessage(const MTPMessage &m);
void addSavedGif(DocumentData *doc);

View File

@ -1183,26 +1183,27 @@ RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) :
request(MTPchannels_GetAdminedPublicChannels(
)).done([=](const MTPmessages_Chats &result) {
if (auto chats = Api::getChatsFromMessagesChats(result)) {
for_const (auto &chat, chats->v) {
if (auto peer = App::feedChat(chat)) {
if (!peer->isChannel() || peer->userName().isEmpty()) {
continue;
}
auto row = ChatRow(peer);
row.peer = peer;
row.name.setText(
st::contactsNameStyle,
peer->name,
Ui::NameTextOptions());
row.status.setText(
st::defaultTextStyle,
Messenger::Instance().createInternalLink(
textcmdLink(1, peer->userName())),
Ui::DialogTextOptions());
_rows.push_back(std::move(row));
const auto &chats = result.match([](const auto &data) {
return data.vchats.v;
});
for (const auto &chat : chats) {
if (const auto peer = App::feedChat(chat)) {
if (!peer->isChannel() || peer->userName().isEmpty()) {
continue;
}
auto row = ChatRow(peer);
row.peer = peer;
row.name.setText(
st::contactsNameStyle,
peer->name,
Ui::NameTextOptions());
row.status.setText(
st::defaultTextStyle,
Messenger::Instance().createInternalLink(
textcmdLink(1, peer->userName())),
Ui::DialogTextOptions());
_rows.push_back(std::move(row));
}
}
resize(width(), _rows.size() * _rowHeight);

View File

@ -30,12 +30,7 @@ base::flat_set<not_null<UserData*>> GetAlreadyInFromPeer(PeerData *peer) {
return {};
}
if (const auto chat = peer->asChat()) {
const auto participants = (
chat->participants
) | ranges::view::transform([](auto &&pair) -> not_null<UserData*> {
return pair.first;
});
return { participants.begin(), participants.end() };
return chat->participants;
} else if (const auto channel = peer->asChannel()) {
if (channel->isMegagroup()) {
const auto &participants = channel->mgInfo->lastParticipants;
@ -306,7 +301,7 @@ void AddSpecialBoxController::rebuildChatRows(not_null<ChatData*> chat) {
--count;
}
}
for (const auto [user, v] : participants) {
for (const auto user : participants) {
if (auto row = createRow(user)) {
delegate()->peerListAppendRow(std::move(row));
}
@ -976,7 +971,7 @@ void AddSpecialBoxSearchController::addChatMembers(
return true;
};
for (const auto [user, v] : chat->participants) {
for (const auto user : chat->participants) {
if (allWordsAreFound(user->nameWords())) {
delegate()->peerListSearchAddRow(user);
}

View File

@ -47,13 +47,9 @@ void RemoveAdmin(
)).done([=](const MTPUpdates &result) {
channel->session().api().applyUpdates(result);
channel->applyEditAdmin(user, oldRights, newRights);
if (const auto done = onDone) {
done();
}
onDone();
}).fail([=](const RPCError &error) {
if (const auto fail = onFail) {
fail();
}
onFail();
}).send();
}
@ -69,13 +65,9 @@ void SaveChatAdmin(
MTP_bool(isAdmin)
)).done([=](const MTPBool &result) {
chat->applyEditAdmin(user, isAdmin);
if (const auto done = onDone) {
done();
}
onDone();
}).fail([=](const RPCError &error) {
if (const auto fail = onFail) {
fail();
}
onFail();
}).send();
}
@ -93,9 +85,7 @@ void SaveChannelAdmin(
)).done([=](const MTPUpdates &result) {
channel->session().api().applyUpdates(result);
channel->applyEditAdmin(user, oldRights, newRights);
if (const auto done = onDone) {
done();
}
onDone();
}).fail([=](const RPCError &error) {
if (error.type() == qstr("USER_NOT_MUTUAL_CONTACT")) {
Ui::show(
@ -115,9 +105,7 @@ void SaveChannelAdmin(
: lng_error_admin_limit_channel)),
LayerOption::KeepOther);
}
if (const auto fail = onFail) {
fail();
}
onFail();
}).send();
}
@ -133,16 +121,11 @@ void SaveChannelRestriction(
user->inputUser,
newRights
)).done([=](const MTPUpdates &result) {
const auto done = onDone;
channel->session().api().applyUpdates(result);
channel->applyEditBanned(user, oldRights, newRights);
if (const auto done = onDone) {
done();
}
onDone();
}).fail([=](const RPCError &error) {
if (const auto fail = onFail) {
fail();
}
onFail();
}).send();
}
@ -368,12 +351,7 @@ void ParticipantsAdditionalData::fillFromChat(not_null<ChatData*> chat) {
if (chat->participants.empty()) {
return;
}
const auto keys = ranges::view::all(
chat->participants
) | ranges::view::transform([](auto &&pair) {
return pair.first;
});
_members = { keys.begin(), keys.end() };
_members = chat->participants;
_admins = chat->admins;
}
@ -794,7 +772,7 @@ void ParticipantsBoxController::addNewParticipants() {
auto already = std::vector<not_null<UserData*>>();
already.reserve(count);
for (auto i = 0; i != count; ++i) {
already.push_back(
already.emplace_back(
delegate()->peerListRowAt(i)->peer()->asUser());
}
AddParticipantsBoxController::Start(
@ -805,16 +783,18 @@ void ParticipantsBoxController::addNewParticipants() {
}
}
void ParticipantsBoxController::peerListSearchAddRow(not_null<PeerData*> peer) {
void ParticipantsBoxController::peerListSearchAddRow(
not_null<PeerData*> peer) {
PeerListController::peerListSearchAddRow(peer);
if (_role == Role::Restricted && delegate()->peerListFullRowsCount() > 0) {
if (_role == Role::Restricted
&& delegate()->peerListFullRowsCount() > 0) {
setDescriptionText(QString());
}
}
std::unique_ptr<PeerListRow> ParticipantsBoxController::createSearchRow(
not_null<PeerData*> peer) {
if (auto user = peer->asUser()) {
if (const auto user = peer->asUser()) {
return createRow(user);
}
return nullptr;
@ -822,13 +802,14 @@ std::unique_ptr<PeerListRow> ParticipantsBoxController::createSearchRow(
std::unique_ptr<PeerListRow> ParticipantsBoxController::createRestoredRow(
not_null<PeerData*> peer) {
if (auto user = peer->asUser()) {
if (const auto user = peer->asUser()) {
return createRow(user);
}
return nullptr;
}
std::unique_ptr<PeerListState> ParticipantsBoxController::saveState() const {
auto ParticipantsBoxController::saveState() const
-> std::unique_ptr<PeerListState> {
Expects(_role == Role::Profile);
auto result = PeerListController::saveState();
@ -837,14 +818,19 @@ std::unique_ptr<PeerListState> ParticipantsBoxController::saveState() const {
my->offset = _offset;
my->allLoaded = _allLoaded;
my->wasLoading = (_loadRequestId != 0);
if (auto search = searchController()) {
if (const auto search = searchController()) {
my->searchState = search->saveState();
}
if (_peer->isMegagroup()) {
const auto channel = _peer->asChannel();
auto weak = result.get();
const auto weak = result.get();
if (const auto chat = _peer->asChat()) {
Notify::PeerUpdateViewer(
chat,
Notify::PeerUpdate::Flag::MembersChanged
) | rpl::start_with_next([=](const Notify::PeerUpdate &) {
weak->controllerState = nullptr;
}, my->lifetime);
} else if (const auto channel = _peer->asMegagroup()) {
channel->owner().megagroupParticipantAdded(
channel
) | rpl::start_with_next([=](not_null<UserData*> user) {
@ -855,7 +841,7 @@ std::unique_ptr<PeerListState> ParticipantsBoxController::saveState() const {
}
auto pos = ranges::find(weak->list, user);
if (pos == weak->list.cend()) {
weak->list.push_back(user);
weak->list.emplace_back(user);
}
ranges::stable_partition(
weak->list,
@ -884,15 +870,15 @@ void ParticipantsBoxController::restoreState(
auto typeErasedState = state
? state->controllerState.get()
: nullptr;
if (auto my = dynamic_cast<SavedState*>(typeErasedState)) {
if (auto requestId = base::take(_loadRequestId)) {
if (const auto my = dynamic_cast<SavedState*>(typeErasedState)) {
if (const auto requestId = base::take(_loadRequestId)) {
request(requestId).cancel();
}
_additional = std::move(my->additional);
_offset = my->offset;
_allLoaded = my->allLoaded;
if (auto search = searchController()) {
if (const auto search = searchController()) {
search->restoreState(std::move(my->searchState));
}
if (my->wasLoading) {
@ -989,13 +975,8 @@ void ParticipantsBoxController::rebuildChatRows(not_null<ChatData*> chat) {
void ParticipantsBoxController::rebuildChatParticipants(
not_null<ChatData*> chat) {
if (chat->participants.empty()) {
// We get such updates often
// (when participants list was invalidated).
//while (delegate()->peerListFullRowsCount() > 0) {
// delegate()->peerListRemoveRow(
// delegate()->peerListRowAt(0));
//}
if (chat->noParticipantInfo()) {
chat->updateFullForced();
return;
}
@ -1011,7 +992,7 @@ void ParticipantsBoxController::rebuildChatParticipants(
--count;
}
}
for (const auto [user, v] : participants) {
for (const auto user : participants) {
if (auto row = createRow(user)) {
delegate()->peerListAppendRow(std::move(row));
}
@ -1036,7 +1017,7 @@ void ParticipantsBoxController::rebuildChatAdmins(
auto list = ranges::view::all(chat->admins) | ranges::to_vector;
if (const auto creator = chat->owner().userLoaded(chat->creator)) {
list.push_back(creator);
list.emplace_back(creator);
}
ranges::sort(list, [](not_null<UserData*> a, not_null<UserData*> b) {
return (a->name.compare(b->name, Qt::CaseInsensitive) < 0);

View File

@ -1175,11 +1175,11 @@ void ShareGameScoreByHash(const QString &hash) {
} else {
auto requestChannelIds = MTP_vector<MTPInputChannel>(1, MTP_inputChannel(MTP_int(channelId), MTP_long(channelAccessHash)));
auto requestChannel = MTPchannels_GetChannels(requestChannelIds);
MTP::send(requestChannel, rpcDone([channelId, resolveMessageAndShareScore](const MTPmessages_Chats &result) {
if (auto chats = Api::getChatsFromMessagesChats(result)) {
App::feedChats(*chats);
}
if (auto channel = App::channelLoaded(channelId)) {
MTP::send(requestChannel, rpcDone([=](const MTPmessages_Chats &result) {
result.match([](const auto &data) {
App::feedChats(data.vchats);
});
if (const auto channel = App::channelLoaded(channelId)) {
resolveMessageAndShareScore(channel);
}
}));

View File

@ -194,7 +194,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
if (_chat->noParticipantInfo()) {
Auth().api().requestFullPeer(_chat);
} else if (!_chat->participants.empty()) {
for (const auto [user, v] : _chat->participants) {
for (const auto user : _chat->participants) {
if (user->isInaccessible()) continue;
if (!listAllSuggestions && filterNotPassedByName(user)) continue;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
@ -253,7 +253,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
if (_chat->noParticipantInfo()) {
Auth().api().requestFullPeer(_chat);
} else if (!_chat->participants.empty()) {
for (const auto [user, version] : _chat->participants) {
for (const auto user : _chat->participants) {
if (!user->botInfo) continue;
if (!user->botInfo->inited) {
Auth().api().requestFullPeer(user);

View File

@ -157,7 +157,7 @@ void Changelogs::addLocalLog(const QString &text) {
};
void Changelogs::addBetaLogs() {
for (const auto[version, changes] : BetaLogs()) {
for (const auto [version, changes] : BetaLogs()) {
addBetaLog(version, changes);
}
}

View File

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_feed.h"
#include "observer_peer.h"
#include "auth_session.h"
#include "apiwrap.h"
namespace {
@ -482,3 +483,28 @@ void ChannelData::setDefaultRestrictions(const MTPChatBannedRights &rights) {
_defaultRestrictions.set(rights.c_chatBannedRights().vflags.v);
Notify::peerUpdatedDelayed(this, UpdateFlag::RightsChanged);
}
auto ChannelData::applyUpdateVersion(int version) -> UpdateStatus {
if (_version > version) {
return UpdateStatus::TooOld;
} else if (_version + 1 < version) {
session().api().requestPeer(this);
return UpdateStatus::Skipped;
}
setVersion(version);
return UpdateStatus::Good;
}
namespace Data {
void ApplyChannelUpdate(
not_null<ChannelData*> channel,
const MTPDupdateChatDefaultBannedRights &update) {
if (channel->applyUpdateVersion(update.vversion.v)
!= ChannelData::UpdateStatus::Good) {
return;
}
channel->setDefaultRestrictions(update.vdefault_banned_rights);
}
} // namespace Data

View File

@ -326,6 +326,19 @@ public:
return _feed;
}
enum class UpdateStatus {
Good,
TooOld,
Skipped,
};
int version() const {
return _version;
}
void setVersion(int version) {
_version = version;
}
UpdateStatus applyUpdateVersion(int version);
// Still public data members.
uint64 access = 0;
@ -334,7 +347,6 @@ public:
QString username;
int32 date = 0;
int version = 0;
std::unique_ptr<MegagroupInfo> mgInfo;
UserId inviter = 0; // > 0 - user who invited me to channel, < 0 - not in channel
@ -354,6 +366,7 @@ private:
int _restrictedCount = 0;
int _kickedCount = 0;
MsgId _availableMinId = 0;
int _version = 0;
RestrictionFlags _defaultRestrictions;
AdminRightFlags _adminRights;
@ -368,3 +381,11 @@ private:
rpl::lifetime _lifetime;
};
namespace Data {
void ApplyChannelUpdate(
not_null<ChannelData*> channel,
const MTPDupdateChatDefaultBannedRights &update);
} // namespace Data

View File

@ -7,6 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "history/history.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "observer_peer.h"
namespace {
@ -113,10 +118,10 @@ void ChatData::applyEditAdmin(not_null<UserData*> user, bool isAdmin) {
}
void ChatData::invalidateParticipants() {
// #TODO groups
participants.clear();
admins.clear();
//removeFlags(MTPDchat::Flag::f_admin);
setAdminRights(MTP_chatAdminRights(MTP_flags(0)));
//setDefaultRestrictions(MTP_chatBannedRights(MTP_flags(0), MTP_int(0)));
invitedByMe.clear();
botStatus = 0;
Notify::peerUpdatedDelayed(
@ -150,3 +155,242 @@ void ChatData::setDefaultRestrictions(const MTPChatBannedRights &rights) {
_defaultRestrictions.set(rights.c_chatBannedRights().vflags.v);
Notify::peerUpdatedDelayed(this, UpdateFlag::RightsChanged);
}
void ChatData::refreshBotStatus() {
if (participants.empty()) {
botStatus = 0;
} else {
const auto bot = ranges::find_if(participants, &UserData::isBot);
botStatus = (bot == end(participants)) ? -1 : 2;
}
}
auto ChatData::applyUpdateVersion(int version) -> UpdateStatus {
if (_version > version) {
return UpdateStatus::TooOld;
} else if (_version + 1 < version) {
invalidateParticipants();
session().api().requestPeer(this);
return UpdateStatus::Skipped;
}
setVersion(version);
return UpdateStatus::Good;
}
namespace Data {
void ApplyChatUpdate(
not_null<ChatData*> chat,
const MTPDupdateChatParticipants &update) {
ApplyChatParticipants(chat, update.vparticipants);
}
void ApplyChatUpdate(
not_null<ChatData*> chat,
const MTPDupdateChatParticipantAdd &update) {
if (chat->applyUpdateVersion(update.vversion.v)
!= ChatData::UpdateStatus::Good) {
return;
} else if (chat->count < 0) {
return;
}
const auto user = chat->owner().userLoaded(update.vuser_id.v);
if (!user
|| (!chat->participants.empty()
&& chat->participants.contains(user))) {
chat->invalidateParticipants();
++chat->count;
return;
}
if (chat->participants.empty()) {
if (chat->count > 0) { // If the count is known.
++chat->count;
}
chat->botStatus = 0;
} else {
chat->participants.emplace(user);
if (update.vinviter_id.v == chat->session().userId()) {
chat->invitedByMe.insert(user);
} else {
chat->invitedByMe.remove(user);
}
++chat->count;
if (user->isBot()) {
chat->botStatus = 2;
if (!user->botInfo->inited) {
chat->session().api().requestFullPeer(user);
}
}
}
Notify::peerUpdatedDelayed(
chat,
Notify::PeerUpdate::Flag::MembersChanged);
}
void ApplyChatUpdate(
not_null<ChatData*> chat,
const MTPDupdateChatParticipantDelete &update) {
if (chat->applyUpdateVersion(update.vversion.v)
!= ChatData::UpdateStatus::Good) {
return;
} else if (chat->count <= 0) {
return;
}
const auto user = chat->owner().userLoaded(update.vuser_id.v);
if (!user
|| (!chat->participants.empty()
&& !chat->participants.contains(user))) {
chat->invalidateParticipants();
--chat->count;
return;
}
if (chat->participants.empty()) {
if (chat->count > 0) {
chat->count--;
}
chat->botStatus = 0;
} else {
chat->participants.erase(user);
chat->count--;
chat->invitedByMe.remove(user);
chat->admins.remove(user);
if (user->isSelf()) {
chat->setAdminRights(MTP_chatAdminRights(MTP_flags(0)));
}
if (const auto history = chat->owner().historyLoaded(chat)) {
if (history->lastKeyboardFrom == user->id) {
history->clearLastKeyboard();
}
}
if (chat->botStatus > 0 && user->botInfo) {
chat->refreshBotStatus();
}
}
Notify::peerUpdatedDelayed(
chat,
Notify::PeerUpdate::Flag::MembersChanged);
}
void ApplyChatUpdate(
not_null<ChatData*> chat,
const MTPDupdateChatParticipantAdmin &update) {
if (chat->applyUpdateVersion(update.vversion.v)
!= ChatData::UpdateStatus::Good) {
return;
}
const auto user = chat->owner().userLoaded(update.vuser_id.v);
if (!user) {
chat->invalidateParticipants();
return;
}
if (user->isSelf()) {
chat->setAdminRights(MTP_chatAdminRights(mtpIsTrue(update.vis_admin)
? MTP_flags(ChatData::DefaultAdminRights())
: MTP_flags(0)));
}
if (mtpIsTrue(update.vis_admin)) {
if (chat->noParticipantInfo()) {
chat->session().api().requestFullPeer(chat);
} else {
chat->admins.emplace(user);
}
} else {
chat->admins.erase(user);
}
Notify::peerUpdatedDelayed(
chat,
Notify::PeerUpdate::Flag::AdminsChanged);
}
void ApplyChatUpdate(
not_null<ChatData*> chat,
const MTPDupdateChatDefaultBannedRights &update) {
if (chat->applyUpdateVersion(update.vversion.v)
!= ChatData::UpdateStatus::Good) {
return;
}
chat->setDefaultRestrictions(update.vdefault_banned_rights);
}
void ApplyChatParticipants(
not_null<ChatData*> chat,
const MTPChatParticipants &participants) {
participants.match([&](const MTPDchatParticipantsForbidden &data) {
if (data.has_self_participant()) {
// data.vself_participant.
}
chat->count = -1;
chat->invalidateParticipants();
}, [&](const MTPDchatParticipants &data) {
const auto status = chat->applyUpdateVersion(data.vversion.v);
if (status == ChatData::UpdateStatus::TooOld) {
return;
}
// Even if we skipped some updates, we got current participants
// and we've requested peer from API to have current rights.
chat->setVersion(data.vversion.v);
const auto &list = data.vparticipants.v;
chat->count = list.size();
chat->participants.clear();
chat->invitedByMe.clear();
chat->admins.clear();
chat->setAdminRights(MTP_chatAdminRights(MTP_flags(0)));
const auto selfUserId = chat->session().userId();
for (const auto &participant : list) {
const auto userId = participant.match([&](const auto &data) {
return data.vuser_id.v;
});
const auto user = chat->owner().userLoaded(userId);
if (!user) {
chat->invalidateParticipants();
break;
}
chat->participants.emplace(user);
const auto inviterId = participant.match([&](
const MTPDchatParticipantCreator &data) {
return 0;
}, [&](const auto &data) {
return data.vinviter_id.v;
});
if (inviterId == selfUserId) {
chat->invitedByMe.insert(user);
}
participant.match([&](const MTPDchatParticipantCreator &data) {
chat->creator = userId;
}, [&](const MTPDchatParticipantAdmin &data) {
chat->admins.emplace(user);
if (user->isSelf()) {
chat->setAdminRights(MTP_chatAdminRights(
MTP_flags(ChatData::DefaultAdminRights())));
}
}, [](const MTPDchatParticipant &) {
});
}
if (chat->participants.empty()) {
return;
}
if (const auto history = chat->owner().historyLoaded(chat)) {
if (history->lastKeyboardFrom) {
const auto i = ranges::find(
chat->participants,
history->lastKeyboardFrom,
&UserData::id);
if (i == end(chat->participants)) {
history->clearLastKeyboard();
}
}
}
chat->refreshBotStatus();
Notify::peerUpdatedDelayed(
chat,
Notify::PeerUpdate::Flag::MembersChanged
| Notify::PeerUpdate::Flag::AdminsChanged);
});
}
} // namespace Data

View File

@ -141,6 +141,20 @@ public:
QString inviteLink() const {
return _inviteLink;
}
void refreshBotStatus();
enum class UpdateStatus {
Good,
TooOld,
Skipped,
};
int version() const {
return _version;
}
void setVersion(int version) {
_version = version;
}
UpdateStatus applyUpdateVersion(int version);
// Still public data members.
MTPint inputChat;
@ -149,10 +163,9 @@ public:
int count = 0;
TimeId date = 0;
int version = 0;
UserId creator = 0;
base::flat_map<not_null<UserData*>, int> participants;
base::flat_set<not_null<UserData*>> participants;
base::flat_set<not_null<UserData*>> invitedByMe;
base::flat_set<not_null<UserData*>> admins;
std::deque<not_null<UserData*>> lastAuthors;
@ -169,5 +182,30 @@ private:
RestrictionFlags _defaultRestrictions;
AdminRightFlags _adminRights;
int _version = 0;
};
namespace Data {
void ApplyChatUpdate(
not_null<ChatData*> chat,
const MTPDupdateChatParticipants &update);
void ApplyChatUpdate(
not_null<ChatData*> chat,
const MTPDupdateChatParticipantAdd &update);
void ApplyChatUpdate(
not_null<ChatData*> chat,
const MTPDupdateChatParticipantDelete &update);
void ApplyChatUpdate(
not_null<ChatData*> chat,
const MTPDupdateChatParticipantAdmin &update);
void ApplyChatUpdate(
not_null<ChatData*> chat,
const MTPDupdateChatDefaultBannedRights &update);
void ApplyChatParticipants(
not_null<ChatData*> chat,
const MTPChatParticipants &participants);
} // namespace Data

View File

@ -350,10 +350,9 @@ bool PeerData::canPinMessages() const {
if (const auto user = asUser()) {
return user->fullFlags() & MTPDuserFull::Flag::f_can_pin_message;
} else if (const auto chat = asChat()) {
// #TODO groups
return !chat->isDeactivated()
/*&& ((chat->adminRights() & ChatAdminRight::f_pin_messages)
|| chat->amCreator())*/;
&& ((chat->adminRights() & ChatAdminRight::f_pin_messages)
|| chat->amCreator());
} else if (const auto channel = asChannel()) {
if (channel->isMegagroup()) {
return (channel->adminRights() & ChatAdminRight::f_pin_messages)

View File

@ -430,9 +430,8 @@ not_null<PeerData*> Session::chat(const MTPChat &data) {
const auto chat = result->asChat();
const auto canAddMembers = chat->canAddMembers();
if (chat->version < data.vversion.v) {
chat->version = data.vversion.v;
if (chat->version() < data.vversion.v) {
chat->setVersion(data.vversion.v);
chat->invalidateParticipants();
}
@ -561,8 +560,8 @@ not_null<PeerData*> Session::chat(const MTPChat &data) {
channel->inputChannel = MTP_inputChannel(data.vid, data.vaccess_hash);
channel->access = data.vaccess_hash.v;
channel->date = data.vdate.v;
if (channel->version < data.vversion.v) {
channel->version = data.vversion.v;
if (channel->version() < data.vversion.v) {
channel->setVersion(data.vversion.v);
}
channel->setUnavailableReason(data.is_restricted()
? ExtractUnavailableReason(qs(data.vrestriction_reason))
@ -657,6 +656,25 @@ PeerData *Session::processChats(const MTPVector<MTPChat> &data) {
return result;
}
void Session::applyMaximumChatVersions(const MTPVector<MTPChat> &data) {
for (const auto &chat : data.v) {
chat.match([&](const MTPDchat &data) {
if (const auto chat = chatLoaded(data.vid.v)) {
if (data.vversion.v < chat->version()) {
chat->setVersion(data.vversion.v);
}
}
}, [&](const MTPDchannel &data) {
if (const auto channel = channelLoaded(data.vid.v)) {
if (data.vversion.v < channel->version()) {
channel->setVersion(data.vversion.v);
}
}
}, [](const auto &) {
});
}
}
PeerData *Session::peerByUsername(const QString &username) const {
const auto uname = username.trimmed();
for (const auto &[peerId, peer] : _peers) {
@ -2372,7 +2390,7 @@ not_null<PollData*> Session::poll(const MTPDmessageMediaPoll &data) {
return result;
}
void Session::applyPollUpdate(const MTPDupdateMessagePoll &update) {
void Session::applyUpdate(const MTPDupdateMessagePoll &update) {
const auto updated = [&] {
const auto i = _polls.find(update.vpoll_id.v);
return (i == end(_polls))
@ -2386,6 +2404,51 @@ void Session::applyPollUpdate(const MTPDupdateMessagePoll &update) {
}
}
void Session::applyUpdate(const MTPDupdateChatParticipants &update) {
const auto chatId = update.vparticipants.match([](const auto &update) {
return update.vchat_id.v;
});
if (const auto chat = chatLoaded(chatId)) {
ApplyChatUpdate(chat, update);
for (const auto user : chat->participants) {
if (user->botInfo && !user->botInfo->inited) {
_session->api().requestFullPeer(user);
}
}
}
}
void Session::applyUpdate(const MTPDupdateChatParticipantAdd &update) {
if (const auto chat = chatLoaded(update.vchat_id.v)) {
ApplyChatUpdate(chat, update);
}
}
void Session::applyUpdate(const MTPDupdateChatParticipantDelete &update) {
if (const auto chat = chatLoaded(update.vchat_id.v)) {
ApplyChatUpdate(chat, update);
}
}
void Session::applyUpdate(const MTPDupdateChatParticipantAdmin &update) {
if (const auto chat = chatLoaded(update.vchat_id.v)) {
ApplyChatUpdate(chat, update);
}
}
void Session::applyUpdate(const MTPDupdateChatDefaultBannedRights &update) {
if (const auto peer = peerLoaded(peerFromMTP(update.vpeer))) {
if (const auto chat = peer->asChat()) {
ApplyChatUpdate(chat, update);
} else if (const auto channel = peer->asChannel()) {
ApplyChannelUpdate(channel, update);
} else {
LOG(("API Error: "
"User received in updateChatDefaultBannedRights."));
}
}
}
not_null<LocationData*> Session::location(const LocationCoords &coords) {
auto i = _locations.find(coords);
if (i == _locations.cend()) {

View File

@ -103,6 +103,8 @@ public:
UserData *processUsers(const MTPVector<MTPUser> &data);
PeerData *processChats(const MTPVector<MTPChat> &data);
void applyMaximumChatVersions(const MTPVector<MTPChat> &data);
void enumerateUsers(Fn<void(not_null<UserData*>)> action) const;
void enumerateGroups(Fn<void(not_null<PeerData*>)> action) const;
void enumerateChannels(Fn<void(not_null<ChannelData*>)> action) const;
@ -270,6 +272,13 @@ public:
MessageIdsList itemsToIds(const HistoryItemsList &items) const;
MessageIdsList itemOrItsGroup(not_null<HistoryItem*> item) const;
void applyUpdate(const MTPDupdateMessagePoll &update);
void applyUpdate(const MTPDupdateChatParticipants &update);
void applyUpdate(const MTPDupdateChatParticipantAdd &update);
void applyUpdate(const MTPDupdateChatParticipantDelete &update);
void applyUpdate(const MTPDupdateChatParticipantAdmin &update);
void applyUpdate(const MTPDupdateChatDefaultBannedRights &update);
int pinnedDialogsCount() const;
const std::deque<Dialogs::Key> &pinnedDialogsOrder() const;
void setPinnedDialog(const Dialogs::Key &key, bool pinned);
@ -403,7 +412,6 @@ public:
not_null<PollData*> poll(PollId id);
not_null<PollData*> poll(const MTPPoll &data);
not_null<PollData*> poll(const MTPDmessageMediaPoll &data);
void applyPollUpdate(const MTPDupdateMessagePoll &update);
not_null<LocationData*> location(const LocationCoords &coords);

View File

@ -122,6 +122,9 @@ public:
bool isBotInlineGeo() const {
return flags() & MTPDuser::Flag::f_bot_inline_geo;
}
bool isBot() const {
return botInfo != nullptr;
}
bool isInaccessible() const {
constexpr auto inaccessible = 0
| MTPDuser::Flag::f_deleted;

View File

@ -96,7 +96,7 @@ void ChatSearchFromController::rebuildRows() {
if (_chat->noParticipantInfo()) {
Auth().api().requestFullPeer(_chat);
} else if (!_chat->participants.empty()) {
for (const auto [user, version] : _chat->participants) {
for (const auto user : _chat->participants) {
ordered.insertMulti(byOnline(user), user);
}
}

View File

@ -506,8 +506,9 @@ bool HistoryItem::canDeleteForEveryone(TimeId now) const {
}
if (!out()) {
if (const auto chat = peer->asChat()) {
// #TODO groups
if (!chat->amCreator()/* && (!chat->amAdmin() || !chat->adminsEnabled())*/) {
if (!chat->amCreator()
&& !(chat->adminRights()
& ChatAdminRight::f_delete_messages)) {
return false;
}
} else if (peer->isUser()) {

View File

@ -758,7 +758,7 @@ void TopBarWidget::updateOnlineDisplay() {
const auto self = Auth().user();
auto online = 0;
auto onlyMe = true;
for (const auto [user, v] : chat->participants) {
for (const auto user : chat->participants) {
if (user->onlineTill > now) {
++online;
if (onlyMe && user != self) onlyMe = false;
@ -826,7 +826,7 @@ void TopBarWidget::updateOnlineDisplayTimer() {
if (const auto user = _activeChat.peer()->asUser()) {
handleUser(user);
} else if (auto chat = _activeChat.peer()->asChat()) {
for (const auto [user, v] : chat->participants) {
for (const auto user : chat->participants) {
handleUser(user);
}
} else if (_activeChat.peer()->isChannel()) {

View File

@ -97,21 +97,21 @@ void ListController::loadMoreRows() {
_preloadRequestId = 0;
_preloadGroupId = 0;
_allLoaded = true;
if (auto chats = Api::getChatsFromMessagesChats(result)) {
auto &list = chats->v;
if (!list.empty()) {
for_const (auto &chatData, list) {
if (auto chat = App::feedChat(chatData)) {
if (!chat->migrateTo()) {
delegate()->peerListAppendRow(
createRow(chat));
}
_preloadGroupId = chat->bareId();
_allLoaded = false;
const auto &chats = result.match([](const auto &data) {
return data.vchats.v;
});
if (!chats.empty()) {
for (const auto &chatData : chats) {
if (const auto chat = App::feedChat(chatData)) {
if (!chat->migrateTo()) {
delegate()->peerListAppendRow(
createRow(chat));
}
_preloadGroupId = chat->bareId();
_allLoaded = false;
}
delegate()->peerListRefreshRows();
}
delegate()->peerListRefreshRows();
}
auto fullCount = delegate()->peerListFullRowsCount();
if (fullCount > kCommonGroupsSearchAfter) {

View File

@ -26,288 +26,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Info {
namespace Profile {
namespace {
constexpr auto kSortByOnlineDelay = TimeMs(1000);
class ChatMembersController
: public PeerListController
, private base::Subscriber
, public base::has_weak_ptr {
public:
ChatMembersController(
not_null<Window::Navigation*> navigation,
not_null<ChatData*> chat);
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
void rowActionClicked(not_null<PeerListRow*> row) override;
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
QWidget *parent,
not_null<PeerListRow*> row) override;
rpl::producer<int> onlineCountValue() const override {
return _onlineCount.value();
}
std::unique_ptr<PeerListRow> createRestoredRow(
not_null<PeerData*> peer) override;
std::unique_ptr<PeerListState> saveState() const override;
void restoreState(std::unique_ptr<PeerListState> state) override;
private:
using Rights = MemberListRow::Rights;
using Type = MemberListRow::Type;
struct SavedState : SavedStateBase {
rpl::lifetime lifetime;
};
void rebuildRows();
void rebuildRowTypes();
void refreshOnlineCount();
std::unique_ptr<PeerListRow> createRow(
not_null<UserData*> user);
void sortByOnline();
void sortByOnlineDelayed();
void removeMember(not_null<UserData*> user);
Type computeType(not_null<UserData*> user);
not_null<Window::Navigation*> _navigation;
not_null<ChatData*> _chat;
base::Timer _sortByOnlineTimer;
rpl::variable<int> _onlineCount = 0;
};
ChatMembersController::ChatMembersController(
not_null<Window::Navigation*> navigation,
not_null<ChatData*> chat)
: PeerListController()
, _navigation(navigation)
, _chat(chat) {
_sortByOnlineTimer.setCallback([this] { sortByOnline(); });
}
void ChatMembersController::prepare() {
setSearchNoResultsText(lang(lng_blocked_list_not_found));
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
delegate()->peerListSetTitle(langFactory(lng_channel_admins));
rebuildRows();
if (!delegate()->peerListFullRowsCount()) {
Auth().api().requestFullPeer(_chat);
}
using UpdateFlag = Notify::PeerUpdate::Flag;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(
UpdateFlag::MembersChanged
| UpdateFlag::UserOnlineChanged
| UpdateFlag::AdminsChanged,
[this](const Notify::PeerUpdate &update) {
if (update.peer == _chat) {
if (update.flags & UpdateFlag::MembersChanged) {
rebuildRows();
}
if (update.flags & UpdateFlag::AdminsChanged) {
rebuildRowTypes();
}
}
if (update.flags & UpdateFlag::UserOnlineChanged) {
if (auto row = delegate()->peerListFindRow(
update.peer->id)) {
row->refreshStatus();
sortByOnlineDelayed();
}
}
}));
}
void ChatMembersController::sortByOnlineDelayed() {
if (!_sortByOnlineTimer.isActive()) {
_sortByOnlineTimer.callOnce(kSortByOnlineDelay);
}
}
void ChatMembersController::sortByOnline() {
auto now = unixtime();
delegate()->peerListSortRows([now](
const PeerListRow &a,
const PeerListRow &b) {
return Data::SortByOnlineValue(a.peer()->asUser(), now) >
Data::SortByOnlineValue(b.peer()->asUser(), now);
});
refreshOnlineCount();
}
std::unique_ptr<PeerListState> ChatMembersController::saveState() const {
auto result = PeerListController::saveState();
auto my = std::make_unique<SavedState>();
using Flag = Notify::PeerUpdate::Flag;
Notify::PeerUpdateViewer(
_chat,
Flag::MembersChanged
) | rpl::start_with_next([state = result.get()](auto update) {
state->controllerState = nullptr;
}, my->lifetime);
result->controllerState = std::move(my);
return result;
}
void ChatMembersController::restoreState(
std::unique_ptr<PeerListState> state) {
PeerListController::restoreState(std::move(state));
sortByOnline();
}
void ChatMembersController::rebuildRows() {
if (_chat->participants.empty()) {
// We get such updates often
// (when participants list was invalidated).
//while (delegate()->peerListFullRowsCount() > 0) {
// delegate()->peerListRemoveRow(
// delegate()->peerListRowAt(0));
//}
return;
}
auto &participants = _chat->participants;
for (auto i = 0, count = delegate()->peerListFullRowsCount();
i != count;) {
auto row = delegate()->peerListRowAt(i);
auto user = row->peer()->asUser();
if (participants.contains(user)) {
++i;
} else {
delegate()->peerListRemoveRow(row);
--count;
}
}
for (const auto [user, v] : participants) {
if (auto row = createRow(user)) {
delegate()->peerListAppendRow(std::move(row));
}
}
sortByOnline();
delegate()->peerListRefreshRows();
}
void ChatMembersController::rebuildRowTypes() {
auto count = delegate()->peerListFullRowsCount();
for (auto i = 0; i != count; ++i) {
auto row = static_cast<MemberListRow*>(
delegate()->peerListRowAt(i).get());
row->setType(computeType(row->user()));
}
delegate()->peerListRefreshRows();
}
void ChatMembersController::refreshOnlineCount() {
auto now = unixtime();
auto left = 0, right = delegate()->peerListFullRowsCount();
while (right > left) {
auto middle = (left + right) / 2;
auto row = delegate()->peerListRowAt(middle);
if (Data::OnlineTextActive(row->peer()->asUser(), now)) {
left = middle + 1;
} else {
right = middle;
}
}
_onlineCount = left;
}
std::unique_ptr<PeerListRow> ChatMembersController::createRestoredRow(
not_null<PeerData*> peer) {
if (auto user = peer->asUser()) {
return createRow(user);
}
return nullptr;
}
std::unique_ptr<PeerListRow> ChatMembersController::createRow(
not_null<UserData*> user) {
return std::make_unique<MemberListRow>(user, computeType(user));
}
auto ChatMembersController::computeType(
not_null<UserData*> user) -> Type {
auto isCreator = (peerFromUser(_chat->creator) == user->id);
// #TODO groups
auto isAdmin = false;/* _chat->adminsEnabled()
&& _chat->admins.contains(user);*/
auto canRemove = false;/* [&] {
if (user->isSelf()) {
return false;
} else if (_chat->amCreator()) {
return true;
} else if (isAdmin || isCreator) {
return false;
} else if (_chat->amAdmin()) {
return true;
} else if (_chat->invitedByMe.contains(user)) {
return true;
}
return false;
}();*/
auto result = Type();
result.rights = isCreator
? Rights::Creator
: isAdmin
? Rights::Admin
: Rights::Normal;
result.canRemove = canRemove;
return result;
}
void ChatMembersController::rowClicked(not_null<PeerListRow*> row) {
_navigation->showPeerInfo(row->peer());
}
void ChatMembersController::rowActionClicked(
not_null<PeerListRow*> row) {
removeMember(row->peer()->asUser());
}
base::unique_qptr<Ui::PopupMenu> ChatMembersController::rowContextMenu(
QWidget *parent,
not_null<PeerListRow*> row) {
auto my = static_cast<MemberListRow*>(row.get());
auto user = my->user();
auto canRemoveMember = my->canRemove();
auto result = base::make_unique_q<Ui::PopupMenu>(parent);
result->addAction(
lang(lng_context_view_profile),
[weak = base::make_weak(this), user] {
if (weak) {
weak->_navigation->showPeerInfo(user);
}
});
if (canRemoveMember) {
result->addAction(
lang(lng_context_remove_from_group),
[weak = base::make_weak(this), user] {
if (weak) {
weak->removeMember(user);
}
});
}
return result;
}
void ChatMembersController::removeMember(not_null<UserData*> user) {
auto text = lng_profile_sure_kick(lt_user, user->firstName);
Ui::show(Box<ConfirmBox>(text, lang(lng_box_remove), [user, chat = _chat] {
Ui::hideLayer();
Auth().api().kickParticipant(chat, user);
Ui::showPeerHistory(chat->id, ShowAtTheEndMsgId);
}));
}
} // namespace
MemberListRow::MemberListRow(
not_null<UserData*> user,
@ -379,17 +97,10 @@ void MemberListRow::paintNameIcon(
std::unique_ptr<PeerListController> CreateMembersController(
not_null<Window::Navigation*> navigation,
not_null<PeerData*> peer) {
if (const auto chat = peer->asChat()) {
return std::make_unique<ChatMembersController>(
navigation,
chat);
} else if (const auto channel = peer->asChannel()) {
return std::make_unique<ParticipantsBoxController>(
navigation,
channel,
ParticipantsBoxController::Role::Profile);
}
Unexpected("Peer type in CreateMembersController()");
return std::make_unique<ParticipantsBoxController>(
navigation,
peer,
ParticipantsBoxController::Role::Profile);
}
} // namespace Profile

View File

@ -4182,7 +4182,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
} break;
case mtpc_updateMessagePoll: {
Auth().data().applyPollUpdate(update.c_updateMessagePoll());
Auth().data().applyUpdate(update.c_updateMessagePoll());
} break;
case mtpc_updateUserTyping: {
@ -4216,47 +4216,23 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
} break;
case mtpc_updateChatParticipants: {
App::feedParticipants(update.c_updateChatParticipants().vparticipants, true);
Auth().data().applyUpdate(update.c_updateChatParticipants());
} break;
case mtpc_updateChatParticipantAdd: {
App::feedParticipantAdd(update.c_updateChatParticipantAdd());
Auth().data().applyUpdate(update.c_updateChatParticipantAdd());
} break;
case mtpc_updateChatParticipantDelete: {
App::feedParticipantDelete(update.c_updateChatParticipantDelete());
} break;
case mtpc_updateChatDefaultBannedRights: {
const auto &data = update.c_updateChatDefaultBannedRights();
const auto peerId = peerFromMTP(data.vpeer);
if (const auto peer = Auth().data().peerLoaded(peerId)) {
if (const auto chat = peer->asChat()) {
if (data.vversion.v == chat->version + 1) {
chat->setDefaultRestrictions(
data.vdefault_banned_rights);
} else {
chat->version = data.vversion.v;
chat->invalidateParticipants();
Auth().api().requestPeer(chat);
}
} else if (const auto channel = peer->asChannel()) {
if (data.vversion.v == channel->version + 1) {
channel->setDefaultRestrictions(
data.vdefault_banned_rights);
} else {
channel->version = data.vversion.v;
Auth().api().requestPeer(channel);
}
} else {
LOG(("API Error: "
"User received in updateChatDefaultBannedRights."));
}
}
Auth().data().applyUpdate(update.c_updateChatParticipantDelete());
} break;
case mtpc_updateChatParticipantAdmin: {
App::feedParticipantAdmin(update.c_updateChatParticipantAdmin());
Auth().data().applyUpdate(update.c_updateChatParticipantAdmin());
} break;
case mtpc_updateChatDefaultBannedRights: {
Auth().data().applyUpdate(update.c_updateChatDefaultBannedRights());
} break;
case mtpc_updateUserStatus: {

View File

@ -411,7 +411,9 @@ template <typename Flags>
class MTPflags {
public:
Flags v = 0;
static_assert(sizeof(Flags) == sizeof(int32), "MTPflags are allowed only wrapping int32 flag types!");
static_assert(
sizeof(Flags) == sizeof(int32),
"MTPflags are allowed only wrapping int32 flag types!");
MTPflags() = default;
MTPflags(internal::ZeroFlagsHelper helper) {

View File

@ -179,13 +179,13 @@ void GroupMembersWidget::updateItemStatusText(Item *item) {
void GroupMembersWidget::refreshMembers() {
_now = unixtime();
if (auto chat = peer()->asChat()) {
if (const auto chat = peer()->asChat()) {
checkSelfAdmin(chat);
if (chat->noParticipantInfo()) {
Auth().api().requestFullPeer(chat);
}
fillChatMembers(chat);
} else if (auto megagroup = peer()->asMegagroup()) {
} else if (const auto megagroup = peer()->asMegagroup()) {
auto &megagroupInfo = megagroup->mgInfo;
if (megagroupInfo->lastParticipants.empty() || megagroup->lastParticipantsCountOutdated()) {
Auth().api().requestLastParticipants(megagroup);
@ -197,11 +197,12 @@ void GroupMembersWidget::refreshMembers() {
refreshVisibility();
}
void GroupMembersWidget::checkSelfAdmin(ChatData *chat) {
if (chat->participants.empty()) return;
void GroupMembersWidget::checkSelfAdmin(not_null<ChatData*> chat) {
if (chat->participants.empty()) {
return;
}
// #TODO groups
const auto self = Auth().user();
const auto self = chat->session().user();
//if (chat->amAdmin() && !chat->admins.contains(self)) {
// chat->admins.insert(self);
//} else if (!chat->amAdmin() && chat->admins.contains(self)) {
@ -246,74 +247,95 @@ void GroupMembersWidget::updateOnlineCount() {
}
}
GroupMembersWidget::Member *GroupMembersWidget::addUser(ChatData *chat, UserData *user) {
auto member = computeMember(user);
auto GroupMembersWidget::addUser(
not_null<ChatData*> chat,
not_null<UserData*> user)
-> not_null<Member*> {
const auto member = computeMember(user);
setItemFlags(member, chat);
addItem(member);
return member;
}
void GroupMembersWidget::fillChatMembers(ChatData *chat) {
if (chat->participants.empty()) return;
void GroupMembersWidget::fillChatMembers(not_null<ChatData*> chat) {
if (chat->participants.empty()) {
return;
}
clearItems();
if (!chat->amIn()) return;
if (!chat->amIn()) {
return;
}
_sortByOnline = true;
reserveItemsForSize(chat->participants.size());
addUser(chat, Auth().user())->onlineForSort
addUser(chat, chat->session().user())->onlineForSort
= std::numeric_limits<TimeId>::max();
for (const auto &[user, v] : chat->participants) {
for (const auto user : chat->participants) {
if (!user->isSelf()) {
addUser(chat, user);
}
}
}
void GroupMembersWidget::setItemFlags(Item *item, ChatData *chat) {
void GroupMembersWidget::setItemFlags(
not_null<Item*> item,
not_null<ChatData*> chat) {
using AdminState = Item::AdminState;
auto user = getMember(item)->user();
auto isCreator = (peerFromUser(chat->creator) == item->peer->id);
// #TODO groups
auto isAdmin = false;// chat->adminsEnabled() && chat->admins.contains(user);
auto adminState = isCreator ? AdminState::Creator : isAdmin ? AdminState::Admin : AdminState::None;
const auto user = getMember(item)->user();
const auto isCreator = (peerFromUser(chat->creator) == item->peer->id);
const auto isAdmin = chat->hasAdminRights();
const auto adminState = isCreator
? AdminState::Creator
: isAdmin
? AdminState::Admin
: AdminState::None;
item->adminState = adminState;
if (item->peer->id == Auth().userPeerId()) {
if (item->peer->id == chat->session().userPeerId()) {
item->hasRemoveLink = false;
} else if (chat->amCreator() /*|| (chat->amAdmin() && (adminState == AdminState::None))*/) {
} else if (chat->amCreator()
|| ((chat->adminRights() & ChatAdminRight::f_ban_users)
&& (adminState == AdminState::None))) {
item->hasRemoveLink = true;
} else if (chat->invitedByMe.contains(user) && (adminState == AdminState::None)) {
} else if (chat->invitedByMe.contains(user)
&& (adminState == AdminState::None)) {
item->hasRemoveLink = true;
} else {
item->hasRemoveLink = false;
}
}
GroupMembersWidget::Member *GroupMembersWidget::addUser(ChannelData *megagroup, UserData *user) {
auto member = computeMember(user);
auto GroupMembersWidget::addUser(
not_null<ChannelData*> megagroup,
not_null<UserData*> user)
-> not_null<Member*> {
const auto member = computeMember(user);
setItemFlags(member, megagroup);
addItem(member);
return member;
}
void GroupMembersWidget::fillMegagroupMembers(ChannelData *megagroup) {
Assert(megagroup->mgInfo != nullptr);
if (megagroup->mgInfo->lastParticipants.empty()) return;
void GroupMembersWidget::fillMegagroupMembers(
not_null<ChannelData*> megagroup) {
Expects(megagroup->mgInfo != nullptr);
if (!megagroup->canViewMembers()) {
if (megagroup->mgInfo->lastParticipants.empty()) {
return;
} else if (!megagroup->canViewMembers()) {
clearItems();
return;
}
_sortByOnline = (megagroup->membersCount() > 0 && megagroup->membersCount() <= Global::ChatSizeMax());
_sortByOnline = (megagroup->membersCount() > 0)
&& (megagroup->membersCount() <= Global::ChatSizeMax());
auto &membersList = megagroup->mgInfo->lastParticipants;
if (_sortByOnline) {
clearItems();
reserveItemsForSize(membersList.size());
if (megagroup->amIn()) {
addUser(megagroup, Auth().user())->onlineForSort
addUser(megagroup, megagroup->session().user())->onlineForSort
= std::numeric_limits<TimeId>::max();
}
} else if (membersList.size() >= itemsCount()) {
@ -325,14 +347,14 @@ void GroupMembersWidget::fillMegagroupMembers(ChannelData *megagroup) {
clearItems();
reserveItemsForSize(membersList.size());
}
for_const (auto user, membersList) {
for (const auto user : membersList) {
if (!_sortByOnline || !user->isSelf()) {
addUser(megagroup, user);
}
}
}
bool GroupMembersWidget::addUsersToEnd(ChannelData *megagroup) {
bool GroupMembersWidget::addUsersToEnd(not_null<ChannelData*> megagroup) {
auto &membersList = megagroup->mgInfo->lastParticipants;
auto &itemsList = items();
for (int i = 0, count = itemsList.size(); i < count; ++i) {
@ -347,7 +369,9 @@ bool GroupMembersWidget::addUsersToEnd(ChannelData *megagroup) {
return true;
}
void GroupMembersWidget::setItemFlags(Item *item, ChannelData *megagroup) {
void GroupMembersWidget::setItemFlags(
not_null<Item*> item,
not_null<ChannelData*> megagroup) {
using AdminState = Item::AdminState;
auto amCreator = item->peer->isSelf() && megagroup->amCreator();
auto amAdmin = item->peer->isSelf() && megagroup->hasAdminRights();
@ -355,7 +379,11 @@ void GroupMembersWidget::setItemFlags(Item *item, ChannelData *megagroup) {
auto isAdmin = (adminIt != megagroup->mgInfo->lastAdmins.cend());
auto isCreator = megagroup->mgInfo->creator == item->peer;
auto adminCanEdit = isAdmin && adminIt->second.canEdit;
auto adminState = (amCreator || isCreator) ? AdminState::Creator : (amAdmin || isAdmin) ? AdminState::Admin : AdminState::None;
auto adminState = (amCreator || isCreator)
? AdminState::Creator
: (amAdmin || isAdmin)
? AdminState::Admin
: AdminState::None;
if (item->adminState != adminState) {
item->adminState = adminState;
auto user = item->peer->asUser();
@ -375,7 +403,8 @@ void GroupMembersWidget::setItemFlags(Item *item, ChannelData *megagroup) {
}
}
GroupMembersWidget::Member *GroupMembersWidget::computeMember(UserData *user) {
auto GroupMembersWidget::computeMember(not_null<UserData*> user)
-> not_null<Member*> {
auto it = _membersByUser.constFind(user);
if (it == _membersByUser.cend()) {
auto member = new Member(user);

View File

@ -44,11 +44,11 @@ private:
void removePeer(PeerData *selectedPeer);
void refreshMembers();
void fillChatMembers(ChatData *chat);
void fillMegagroupMembers(ChannelData *megagroup);
void fillChatMembers(not_null<ChatData*> chat);
void fillMegagroupMembers(not_null<ChannelData*> megagroup);
void sortMembers();
void updateOnlineCount();
void checkSelfAdmin(ChatData *chat);
void checkSelfAdmin(not_null<ChatData*> chat);
void preloadMore();
void refreshUserOnline(UserData *user);
@ -66,12 +66,14 @@ private:
}
void updateItemStatusText(Item *item);
Member *computeMember(UserData *user);
Member *addUser(ChatData *chat, UserData *user);
Member *addUser(ChannelData *megagroup, UserData *user);
void setItemFlags(Item *item, ChatData *chat);
void setItemFlags(Item *item, ChannelData *megagroup);
bool addUsersToEnd(ChannelData *megagroup);
not_null<Member*> computeMember(not_null<UserData*> user);
not_null<Member*> addUser(not_null<ChatData*> chat, not_null<UserData*> user);
not_null<Member*> addUser(not_null<ChannelData*> megagroup, not_null<UserData*> user);
void setItemFlags(not_null<Item*> item, not_null<ChatData*> chat);
void setItemFlags(
not_null<Item*> item,
not_null<ChannelData*> megagroup);
bool addUsersToEnd(not_null<ChannelData*> megagroup);
QMap<UserData*, Member*> _membersByUser;
bool _sortByOnline = false;

View File

@ -126,7 +126,7 @@ void writePeer(QDataStream &stream, PeerData *peer) {
<< chat->name
<< qint32(chat->count)
<< qint32(chat->date)
<< qint32(chat->version)
<< qint32(chat->version())
<< qint32(chat->creator)
<< qint32(0)
<< quint32(chat->flags())
@ -136,7 +136,7 @@ void writePeer(QDataStream &stream, PeerData *peer) {
<< channel->name
<< quint64(channel->access)
<< qint32(channel->date)
<< qint32(channel->version)
<< qint32(channel->version())
<< qint32(0)
<< quint32(channel->flags())
<< channel->inviteLink();
@ -226,7 +226,11 @@ PeerData *readPeer(int streamAppVersion, QDataStream &stream) {
chat->setName(name);
chat->count = count;
chat->date = date;
chat->version = version;
// We don't save participants, admin status and banned rights.
// So we don't restore the version field, info is still unknown.
chat->setVersion(0);
chat->creator = creator;
chat->setFlags(MTPDchat::Flags::from_raw(flags));
chat->setInviteLink(inviteLink);
@ -247,7 +251,11 @@ PeerData *readPeer(int streamAppVersion, QDataStream &stream) {
channel->setName(name, QString());
channel->access = access;
channel->date = date;
channel->version = version;
// We don't save participants, admin status and banned rights.
// So we don't restore the version field, info is still unknown.
channel->setVersion(0);
channel->setFlags(MTPDchannel::Flags::from_raw(flags));
channel->setInviteLink(inviteLink);