From fd339e401f7f1e09606eea6b8a0337f466e44aca Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 9 Nov 2015 12:51:22 +0300 Subject: [PATCH] megagroup members support improved --- Telegram/SourceFiles/apiwrap.cpp | 112 ++++++++++++++++++- Telegram/SourceFiles/apiwrap.h | 12 ++ Telegram/SourceFiles/boxes/addcontactbox.cpp | 2 +- Telegram/SourceFiles/boxes/photocropbox.cpp | 19 +++- Telegram/SourceFiles/boxes/photocropbox.h | 5 +- Telegram/SourceFiles/dropdown.cpp | 41 ++++++- Telegram/SourceFiles/dropdown.h | 2 + Telegram/SourceFiles/history.cpp | 107 +++++++++++++----- Telegram/SourceFiles/history.h | 1 + Telegram/SourceFiles/historywidget.cpp | 27 ++--- Telegram/SourceFiles/historywidget.h | 2 +- Telegram/SourceFiles/intro/introsignup.cpp | 2 +- Telegram/SourceFiles/mainwidget.cpp | 10 +- Telegram/SourceFiles/profilewidget.cpp | 104 ++++++++++++----- Telegram/SourceFiles/settingswidget.cpp | 2 +- Telegram/SourceFiles/structs.cpp | 5 +- Telegram/SourceFiles/structs.h | 30 ++++- 17 files changed, 387 insertions(+), 96 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index f1948355d..0433bcd7b 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -286,8 +286,20 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt } else { channel->photoId = 0; } + if (channel->mgInfo) { + if (f.has_migrated_from_chat_id()) { + channel->mgInfo->migrateFrom = App::chat(peerFromChat(f.vmigrated_from_chat_id)); + channel->mgInfo->migrateFrom->migrateTo = channel; + } + } channel->about = qs(f.vabout); - channel->count = f.has_participants_count() ? f.vparticipants_count.v : 0; + int32 newCount = f.has_participants_count() ? f.vparticipants_count.v : 0; + if (newCount != channel->count) { + channel->count = newCount; + if (channel->isMegagroup() && !channel->mgInfo->lastParticipants.isEmpty()) { + requestLastParticipants(channel); + } + } channel->adminsCount = f.has_admins_count() ? f.vadmins_count.v : 0; channel->invitationUrl = (f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString(); if (History *h = App::historyLoaded(channel->id)) { @@ -296,6 +308,9 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt h->inboxReadBefore = f.vread_inbox_max_id.v + 1; h->asChannelHistory()->unreadCountAll = f.vunread_count.v; } + if (channel->mgInfo && channel->mgInfo->migrateFrom) { + h->asChannelHistory()->removeJoinedMessage(); + } } channel->fullUpdated(); @@ -385,6 +400,13 @@ void ApiWrap::requestPeers(const QList &peers) { if (!users.isEmpty()) MTP::send(MTPusers_GetUsers(MTP_vector(users)), rpcDone(&ApiWrap::gotUsers)); } +void ApiWrap::requestLastParticipants(ChannelData *peer) { + if (!peer || !peer->isMegagroup() || _participantsRequests.contains(peer)) return; + mtpRequestId req = MTP::send(MTPchannels_GetParticipants(peer->inputChannel, MTP_channelParticipantsRecent(), MTP_int(0), MTP_int(cMaxGroupCount())), rpcDone(&ApiWrap::lastParticipantsDone, peer), rpcFail(&ApiWrap::lastParticipantsFail, peer)); + _participantsRequests.insert(peer, req); + MTP::send(MTPchannels_GetParticipants(peer->inputChannel, MTP_channelParticipantsBots(), MTP_int(0), MTP_int(cMaxGroupCount())), rpcDone(&ApiWrap::lastParticipantsDone, peer), rpcFail(&ApiWrap::lastParticipantsFail, peer)); +} + void ApiWrap::gotChat(PeerData *peer, const MTPmessages_Chats &result) { _peerRequests.remove(peer); @@ -433,6 +455,64 @@ bool ApiWrap::gotPeerFailed(PeerData *peer, const RPCError &error) { return true; } +void ApiWrap::lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelParticipants &result, mtpRequestId req) { + bool bots = (_participantsRequests.value(peer) != req); + + if (!bots) { + _participantsRequests.remove(peer); + } + if (!peer->mgInfo) return; + + if (result.type() == mtpc_channels_channelParticipants) { + const MTPDchannels_channelParticipants &d(result.c_channels_channelParticipants()); + const QVector &v(d.vparticipants.c_vector().v); + App::feedUsers(d.vusers); + int32 botStatus = peer->mgInfo->botStatus; + if (bots) { + peer->mgInfo->bots.clear(); + botStatus = -1; + } else { + peer->mgInfo->lastParticipants.clear(); + peer->mgInfo->lastAdmins.clear(); + } + for (QVector::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) { + int32 userId = 0; + bool admin = false; + + switch (i->type()) { + case mtpc_channelParticipant: userId = i->c_channelParticipant().vuser_id.v; break; + case mtpc_channelParticipantSelf: userId = i->c_channelParticipantSelf().vuser_id.v; break; + case mtpc_channelParticipantModerator: userId = i->c_channelParticipantModerator().vuser_id.v; break; + case mtpc_channelParticipantEditor: userId = i->c_channelParticipantEditor().vuser_id.v; admin = true; break; + case mtpc_channelParticipantKicked: userId = i->c_channelParticipantKicked().vuser_id.v; break; + case mtpc_channelParticipantCreator: userId = i->c_channelParticipantCreator().vuser_id.v; admin = true; break; + } + UserData *u = App::user(userId); + if (bots) { + if (u->botInfo) { + peer->mgInfo->bots.insert(u, true); + botStatus = (botStatus > 0/* || i.key()->botInfo->readsAllHistory*/) ? 2 : 1; + } + } else { + peer->mgInfo->lastParticipants.push_back(u); + if (admin) peer->mgInfo->lastAdmins.insert(u, true); + } + } + if (d.vcount.v > peer->count) { + peer->count = d.vcount.v; + } else if (v.count() > peer->count) { + peer->count = v.count(); + } + if (App::main()) emit fullPeerUpdated(peer); + } +} + +bool ApiWrap::lastParticipantsFail(ChannelData *peer, const RPCError &error) { + if (mtpIsFlood(error)) return false; + _participantsRequests.remove(peer); + return true; +} + void ApiWrap::requestSelfParticipant(ChannelData *channel) { if (_selfParticipantRequests.contains(channel)) return; _selfParticipantRequests.insert(channel, MTP::send(MTPchannels_GetParticipant(channel->inputChannel, MTP_inputUserSelf()), rpcDone(&ApiWrap::gotSelfParticipant, channel), rpcFail(&ApiWrap::gotSelfParticipantFail, channel), 0, 5)); @@ -487,6 +567,36 @@ bool ApiWrap::gotSelfParticipantFail(ChannelData *channel, const RPCError &error return true; } +void ApiWrap::kickParticipant(PeerData *peer, UserData *user) { + KickRequest req(peer, user); + if (_kickRequests.contains(req)); + if (peer->isChannel()) { + _kickRequests.insert(req, MTP::send(MTPchannels_KickFromChannel(peer->asChannel()->inputChannel, user->inputUser, MTP_bool(true)), rpcDone(&ApiWrap::kickParticipantDone, req), rpcFail(&ApiWrap::kickParticipantFail, req))); + } +} + +void ApiWrap::kickParticipantDone(KickRequest kick, const MTPUpdates &result, mtpRequestId req) { + _kickRequests.remove(kick); + if (kick.first->isMegagroup()) { + int32 i = kick.first->asChannel()->mgInfo->lastParticipants.indexOf(kick.second); + if (i >= 0) { + kick.first->asChannel()->mgInfo->lastParticipants.removeAt(i); + kick.first->asChannel()->mgInfo->lastAdmins.remove(kick.second); + kick.first->asChannel()->mgInfo->bots.remove(kick.second); + } + if (kick.first->asChannel()->count > 1) { + kick.first->asChannel()->count--; + } + } + emit fullPeerUpdated(kick.first); +} + +bool ApiWrap::kickParticipantFail(KickRequest kick, const RPCError &error, mtpRequestId req) { + if (mtpIsFlood(error)) return false; + _kickRequests.remove(kick); + return true; +} + void ApiWrap::scheduleStickerSetRequest(uint64 setId, uint64 access) { if (!_stickerSetRequests.contains(setId)) { _stickerSetRequests.insert(setId, qMakePair(access, 0)); diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 4529d935f..1f2a6be95 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -36,11 +36,13 @@ public: void requestFullPeer(PeerData *peer); void requestPeer(PeerData *peer); void requestPeers(const QList &peers); + void requestLastParticipants(ChannelData *peer); void processFullPeer(PeerData *peer, const MTPmessages_ChatFull &result); void processFullPeer(PeerData *peer, const MTPUserFull &result); void requestSelfParticipant(ChannelData *channel); + void kickParticipant(PeerData *peer, UserData *user); void requestWebPageDelayed(WebPageData *page); void clearWebPageRequest(WebPageData *page); @@ -91,6 +93,16 @@ private: bool gotPeerFailed(PeerData *peer, const RPCError &err); PeerRequests _peerRequests; + void lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelParticipants &result, mtpRequestId req); + bool lastParticipantsFail(ChannelData *peer, const RPCError &error); + PeerRequests _participantsRequests; + + typedef QPair KickRequest; + typedef QMap KickRequests; + void kickParticipantDone(KickRequest kick, const MTPUpdates &updates, mtpRequestId req); + bool kickParticipantFail(KickRequest kick, const RPCError &error, mtpRequestId req); + KickRequests _kickRequests; + void gotSelfParticipant(ChannelData *channel, const MTPchannels_ChannelParticipant &result); bool gotSelfParticipantFail(ChannelData *channel, const RPCError &error); typedef QMap SelfParticipantRequests; diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index 1337087df..def85ee2c 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -593,7 +593,7 @@ void GroupInfoBox::onPhoto() { if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) { return; } - PhotoCropBox *box = new PhotoCropBox(img, (_creating == CreatingGroupChannel) ? peerFromChannel(0) : peerFromChat(0), false); + PhotoCropBox *box = new PhotoCropBox(img, (_creating == CreatingGroupChannel) ? peerFromChannel(0) : peerFromChat(0)); connect(box, SIGNAL(ready(const QImage&)), this, SLOT(onPhotoReady(const QImage&))); App::wnd()->replaceLayer(box); } diff --git a/Telegram/SourceFiles/boxes/photocropbox.cpp b/Telegram/SourceFiles/boxes/photocropbox.cpp index 509451471..e76410712 100644 --- a/Telegram/SourceFiles/boxes/photocropbox.cpp +++ b/Telegram/SourceFiles/boxes/photocropbox.cpp @@ -27,13 +27,26 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "photocropbox.h" #include "fileuploader.h" -PhotoCropBox::PhotoCropBox(const QImage &img, const PeerId &peer, bool upload) : AbstractBox() +PhotoCropBox::PhotoCropBox(const QImage &img, const PeerId &peer) : AbstractBox() , _downState(0) , _done(this, lang(lng_settings_save), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) , _img(img) , _peerId(peer) { - if (peerIsChat(_peerId)) { + init(img, 0); +} + +PhotoCropBox::PhotoCropBox(const QImage &img, PeerData *peer) : AbstractBox() +, _downState(0) +, _done(this, lang(lng_settings_save), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +, _img(img) +, _peerId(peer->id) { + init(img, peer); +} + +void PhotoCropBox::init(const QImage &img, PeerData *peer) { + if (peerIsChat(_peerId) || (peer && peer->isMegagroup())) { _title = lang(lng_create_group_crop); } else if (peerIsChannel(_peerId)) { _title = lang(lng_create_channel_crop); @@ -43,7 +56,7 @@ PhotoCropBox::PhotoCropBox(const QImage &img, const PeerId &peer, bool upload) : connect(&_done, SIGNAL(clicked()), this, SLOT(onSend())); connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - if (_peerId && upload) { + if (peerToBareInt(_peerId)) { connect(this, SIGNAL(ready(const QImage&)), this, SLOT(onReady(const QImage&))); } diff --git a/Telegram/SourceFiles/boxes/photocropbox.h b/Telegram/SourceFiles/boxes/photocropbox.h index c9262a284..347d24064 100644 --- a/Telegram/SourceFiles/boxes/photocropbox.h +++ b/Telegram/SourceFiles/boxes/photocropbox.h @@ -27,7 +27,8 @@ class PhotoCropBox : public AbstractBox { public: - PhotoCropBox(const QImage &img, const PeerId &peer, bool upload = true); + PhotoCropBox(const QImage &img, const PeerId &peer); + PhotoCropBox(const QImage &img, PeerData *peer); void keyPressEvent(QKeyEvent *e); void paintEvent(QPaintEvent *e); void resizeEvent(QResizeEvent *e); @@ -53,6 +54,8 @@ protected: private: + void init(const QImage &img, PeerData *peer); + QString _title; int32 _downState; int32 _thumbx, _thumby, _thumbw, _thumbh; diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index a6c6eb5d1..9b5640828 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -2740,7 +2740,7 @@ void MentionsInner::paintEvent(QPaintEvent *e) { const BotCommand *command = _crows->at(i).second; QString toHighlight = command->command; - int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : -1; + int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1); if (hasUsername || botStatus == 0 || botStatus == 2) { toHighlight += '@' + user->username; } @@ -2828,7 +2828,7 @@ QString MentionsInner::getSelected() const { } else { UserData *user = _crows->at(_sel).first; const BotCommand *command(_crows->at(_sel).second); - int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : -1; + int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1); if (botStatus == 0 || botStatus == 2 || _parent->filter().indexOf('@') > 1) { result = '/' + command->command + '@' + user->username; } else { @@ -3004,6 +3004,19 @@ void MentionsDropdown::updateFiltered(bool toDown) { rows.push_back(i.value()); } } + } else if (_filter.at(0) == '@' && _channel && _channel->isMegagroup()) { + QMultiMap ordered; + if (_channel->mgInfo->lastParticipants.isEmpty()) { + if (App::api()) App::api()->requestLastParticipants(_channel); + } else { + rows.reserve(_channel->mgInfo->lastParticipants.size()); + for (MegagroupInfo::LastParticipants::const_iterator i = _channel->mgInfo->lastParticipants.cbegin(), e = _channel->mgInfo->lastParticipants.cend(); i != e; ++i) { + UserData *user = *i; + if (user->username.isEmpty()) continue; + if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue; + rows.push_back(user); + } + } } else if (_filter.at(0) == '#') { const RecentHashtagPack &recent(cRecentWriteHashtags()); hrows.reserve(recent.size()); @@ -3019,7 +3032,6 @@ void MentionsDropdown::updateFiltered(bool toDown) { if (_chat->noParticipantInfo()) { if (App::api()) App::api()->requestFullPeer(_chat); } else if (!_chat->participants.isEmpty()) { - int32 index = 0; for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) { UserData *user = i.key(); if (!user->botInfo) continue; @@ -3033,10 +3045,23 @@ void MentionsDropdown::updateFiltered(bool toDown) { if (!_user->botInfo->inited && App::api()) App::api()->requestFullPeer(_user); cnt = _user->botInfo->commands.size(); bots.insert(_user, true); + } else if (_channel && _channel->isMegagroup()) { + if (_channel->mgInfo->bots.isEmpty()) { + if (!_channel->mgInfo->botStatus && App::api()) App::api()->requestLastParticipants(_channel); + } else { + for (MegagroupInfo::Bots::const_iterator i = _channel->mgInfo->bots.cbegin(), e = _channel->mgInfo->bots.cend(); i != e; ++i) { + UserData *user = i.key(); + if (!user->botInfo) continue; + if (!user->botInfo->inited && App::api()) App::api()->requestFullPeer(user); + if (user->botInfo->commands.isEmpty()) continue; + bots.insert(user, true); + cnt += user->botInfo->commands.size(); + } + } } if (cnt) { crows.reserve(cnt); - int32 botStatus = _chat ? _chat->botStatus : -1; + int32 botStatus = _chat ? _chat->botStatus : ((_channel && _channel->isMegagroup()) ? _channel->mgInfo->botStatus : -1); if (_chat) { for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) { UserData *user = *i; @@ -3196,6 +3221,14 @@ ChatData *MentionsDropdown::chat() const { return _chat; } +ChannelData *MentionsDropdown::channel() const { + return _channel; +} + +UserData *MentionsDropdown::user() const { + return _user; +} + int32 MentionsDropdown::innerTop() { return _scroll.scrollTop(); } diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h index 6daff9822..25304c82c 100644 --- a/Telegram/SourceFiles/dropdown.h +++ b/Telegram/SourceFiles/dropdown.h @@ -649,6 +649,8 @@ public: const QString &filter() const; ChatData *chat() const; + ChannelData *channel() const; + UserData *user() const; int32 innerTop(); int32 innerBottom(); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 997dbb9ad..11cfd3359 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -133,14 +133,14 @@ namespace { } const TextParseOptions &itemTextOptions(History *h, PeerData *f) { - if ((h->peer->isUser() && h->peer->asUser()->botInfo) || (f->isUser() && f->asUser()->botInfo) || (h->peer->isChat() && h->peer->asChat()->botStatus >= 0) || (h->peer->isChannel() && h->peer->asChannel()->botStatus >= 0)) { + if ((h->peer->isUser() && h->peer->asUser()->botInfo) || (f->isUser() && f->asUser()->botInfo) || (h->peer->isChat() && h->peer->asChat()->botStatus >= 0) || (h->peer->isMegagroup() && h->peer->asChannel()->mgInfo->botStatus >= 0)) { return _historyBotOptions; } return _historyTextOptions; } const TextParseOptions &itemTextNoMonoOptions(History *h, PeerData *f) { - if ((h->peer->isUser() && h->peer->asUser()->botInfo) || (f->isUser() && f->asUser()->botInfo) || (h->peer->isChat() && h->peer->asChat()->botStatus >= 0) || (h->peer->isChannel() && h->peer->asChannel()->botStatus >= 0)) { + if ((h->peer->isUser() && h->peer->asUser()->botInfo) || (f->isUser() && f->asUser()->botInfo) || (h->peer->isChat() && h->peer->asChat()->botStatus >= 0) || (h->peer->isMegagroup() && h->peer->asChannel()->mgInfo->botStatus >= 0)) { return _historyBotNoMonoOptions; } return _historyTextNoMonoOptions; @@ -684,7 +684,9 @@ void ChannelHistory::addNewGroup(const MTPMessageGroup &group) { } HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) { - if (_joinedMessage || !peer->asChannel()->amIn()) return _joinedMessage; + if (_joinedMessage || !peer->asChannel()->amIn() || (peer->asChannel()->mgInfo && peer->asChannel()->mgInfo->migrateFrom)) { + return _joinedMessage; + } UserData *inviter = (peer->asChannel()->inviter > 0) ? App::userLoaded(peer->asChannel()->inviter) : 0; if (!inviter) return 0; @@ -809,7 +811,9 @@ HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) { } void ChannelHistory::checkJoinedMessage(bool createUnread) { - if (_joinedMessage || peer->asChannel()->inviter <= 0) return; + if (_joinedMessage || peer->asChannel()->inviter <= 0 || (peer->asChannel()->mgInfo && peer->asChannel()->mgInfo->migrateFrom)) { + return; + } if (isEmpty()) { if (loadedAtTop() && loadedAtBottom()) { if (insertJoinedMessage(createUnread)) { @@ -857,6 +861,13 @@ void ChannelHistory::checkJoinedMessage(bool createUnread) { } } +void ChannelHistory::removeJoinedMessage() { + if (_joinedMessage) { + _joinedMessage->destroy(); + _joinedMessage = 0; + } +} + void ChannelHistory::checkMaxReadMessageDate() { if (_maxReadMessageDate.isValid()) return; @@ -1713,33 +1724,54 @@ HistoryItem *History::addNewItem(HistoryBlock *to, bool newBlock, HistoryItem *a } } if (adding->from()->id) { - if (peer->isChat() && adding->from()->isUser()) { - QList *lastAuthors = &(peer->asChat()->lastAuthors); - int prev = lastAuthors->indexOf(adding->from()->asUser()); - if (prev > 0) { - lastAuthors->removeAt(prev); + if (adding->from()->isUser()) { + QList *lastAuthors = 0; + if (peer->isChat()) { + lastAuthors = &peer->asChat()->lastAuthors; + } else if (peer->isMegagroup() && !peer->asChannel()->mgInfo->lastParticipants.isEmpty()) { + lastAuthors = &peer->asChannel()->mgInfo->lastParticipants; } - if (prev) { - lastAuthors->push_front(adding->from()->asUser()); + if (lastAuthors) { + int prev = lastAuthors->indexOf(adding->from()->asUser()); + if (prev > 0) { + lastAuthors->removeAt(prev); + } + if (prev) { + lastAuthors->push_front(adding->from()->asUser()); + } } } if (adding->hasReplyMarkup()) { int32 markupFlags = App::replyMarkup(channelId(), adding->id).flags; if (!(markupFlags & MTPDreplyKeyboardMarkup::flag_selective) || adding->mentionsMe()) { + QMap *markupSenders = 0; if (peer->isChat()) { - peer->asChat()->markupSenders.insert(adding->from(), true); + markupSenders = &peer->asChat()->markupSenders; + } else if (peer->isMegagroup()) { + markupSenders = &peer->asChannel()->mgInfo->markupSenders; + } + if (markupSenders) { + markupSenders->insert(adding->from(), true); } if (markupFlags & MTPDreplyKeyboardMarkup_flag_ZERO) { // zero markup means replyKeyboardHide - if (lastKeyboardFrom == adding->from()->id || (!lastKeyboardInited && !peer->isChat() && !adding->out())) { + if (lastKeyboardFrom == adding->from()->id || (!lastKeyboardInited && !peer->isChat() && !peer->isMegagroup() && !adding->out())) { clearLastKeyboard(); } - } else if (peer->isChat() && adding->from()->isUser() && (!peer->asChat()->canWrite() || !peer->asChat()->participants.isEmpty()) && !peer->asChat()->participants.contains(adding->from()->asUser())) { - clearLastKeyboard(); } else { - lastKeyboardInited = true; - lastKeyboardId = adding->id; - lastKeyboardFrom = adding->from()->id; - lastKeyboardUsed = false; + bool botNotInChat = false; + if (peer->isChat()) { + botNotInChat = adding->from()->isUser() && (!peer->canWrite() || !peer->asChat()->participants.isEmpty()) && !peer->asChat()->participants.contains(adding->from()->asUser()); + } else if (peer->isMegagroup()) { + botNotInChat = adding->from()->isUser() && (!peer->canWrite() || !peer->asChannel()->mgInfo->bots.isEmpty()) && !peer->asChannel()->mgInfo->bots.contains(adding->from()->asUser()); + } + if (botNotInChat) { + clearLastKeyboard(); + } else { + lastKeyboardInited = true; + lastKeyboardId = adding->id; + lastKeyboardFrom = adding->from()->id; + lastKeyboardUsed = false; + } } } } @@ -1904,10 +1936,20 @@ void History::addOlderSlice(const QVector &slice, const QVector *lastAuthors = peer->isChat() ? &(peer->asChat()->lastAuthors) : 0; + QList *lastAuthors = 0; + QMap *markupSenders = 0; + if (peer->isChat()) { + lastAuthors = &peer->asChat()->lastAuthors; + markupSenders = &peer->asChat()->markupSenders; + } else if (peer->isMegagroup()) { + if (!peer->asChannel()->mgInfo->lastParticipants.isEmpty()) { + lastAuthors = &peer->asChannel()->mgInfo->lastParticipants; + } + markupSenders = &peer->asChannel()->mgInfo->markupSenders; + } for (int32 i = block->items.size(); i > 0; --i) { HistoryItem *item = block->items[i - 1]; if (!item->indexInOverview()) continue; @@ -1932,16 +1974,24 @@ void History::addOlderSlice(const QVector &slice, const QVectorfrom()->isUser() && !lastAuthors->contains(item->from()->asUser())) { lastAuthors->push_back(item->from()->asUser()); } - if (!lastKeyboardInited && item->hasReplyMarkup() && !item->out()) { // chats with bots + } + if (markupSenders) { // chats with bots + if (!lastKeyboardInited && item->hasReplyMarkup() && !item->out()) { int32 markupFlags = App::replyMarkup(channelId(), item->id).flags; if (!(markupFlags & MTPDreplyKeyboardMarkup::flag_selective) || item->mentionsMe()) { - bool wasKeyboardHide = peer->asChat()->markupSenders.contains(item->from()); + bool wasKeyboardHide = markupSenders->contains(item->from()); if (!wasKeyboardHide) { - peer->asChat()->markupSenders.insert(item->from(), true); + markupSenders->insert(item->from(), true); } if (!(markupFlags & MTPDreplyKeyboardMarkup_flag_ZERO)) { if (!lastKeyboardInited) { - if (wasKeyboardHide || ((!peer->asChat()->canWrite() || !peer->asChat()->participants.isEmpty()) && item->from()->isUser() && !peer->asChat()->participants.contains(item->from()->asUser()))) { + bool botNotInChat = false; + if (peer->isChat()) { + botNotInChat = (!peer->canWrite() || !peer->asChat()->participants.isEmpty()) && item->from()->isUser() && !peer->asChat()->participants.contains(item->from()->asUser()); + } else if (peer->isMegagroup()) { + botNotInChat = (!peer->canWrite() || !peer->asChannel()->mgInfo->bots.isEmpty()) && item->from()->isUser() && !peer->asChannel()->mgInfo->bots.contains(item->from()->asUser()); + } + if (wasKeyboardHide || botNotInChat) { clearLastKeyboard(); } else { lastKeyboardInited = true; @@ -2485,6 +2535,9 @@ void History::clear(bool leaveItems) { peer->asChat()->markupSenders.clear(); } else if (isChannel()) { asChannelHistory()->cleared(); + if (isMegagroup()) { + peer->asChannel()->mgInfo->markupSenders.clear(); + } } if (leaveItems && App::main()) App::main()->historyCleared(this); } @@ -7450,6 +7503,10 @@ void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) { const MTPDmessageActionChannelMigrateFrom &d(action.c_messageActionChannelMigrateFrom()); if (true/*PeerData *chat = App::peerLoaded(peerFromChannel(d.vchat_id))*/) { text = lang(lng_action_group_migrate); + if (history()->peer->asChannel()->mgInfo) { + history()->peer->asChannel()->mgInfo->migrateFrom = App::chat(peerFromChat(d.vchat_id)); + history()->peer->asChannel()->mgInfo->migrateFrom->migrateTo = history()->peer->asChannel(); + } } else { text = lang(lng_contacts_loading); } diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 1188553e6..849aaf894 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -398,6 +398,7 @@ public: HistoryJoined *insertJoinedMessage(bool unread); void checkJoinedMessage(bool createUnread = false); + void removeJoinedMessage(); const QDateTime &maxReadMessageDate(); private: diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 128f5e454..b05ada7e2 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2921,7 +2921,7 @@ void HistoryWidget::applyDraft(bool parseLinks) { } } -void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) { +void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId) { MsgId wasMsgId = _showAtMsgId; History *wasHistory = _history; @@ -3811,9 +3811,9 @@ void HistoryWidget::onVisibleChanged() { void HistoryWidget::onHistoryToEnd() { if (_replyReturn) { - showPeerHistory(_peer->id, _replyReturn->id); + showHistory(_peer->id, _replyReturn->id); } else if (_peer) { - showPeerHistory(_peer->id, ShowAtUnreadMsgId); + showHistory(_peer->id, ShowAtUnreadMsgId); } } @@ -3830,7 +3830,7 @@ void HistoryWidget::onCollapseComments() { } } } - showPeerHistory(_peer->id, switchAt); + showHistory(_peer->id, switchAt); } void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { @@ -4318,7 +4318,7 @@ void HistoryWidget::sendBotCommand(const QString &cmd, MsgId replyTo) { // reply PeerData *bot = _peer->isUser() ? _peer : (App::hoveredLinkItem() ? (App::hoveredLinkItem()->toHistoryForwarded() ? App::hoveredLinkItem()->toHistoryForwarded()->fromForwarded() : App::hoveredLinkItem()->from()) : 0); if (bot && (!bot->isUser() || !bot->asUser()->botInfo)) bot = 0; QString username = bot ? bot->asUser()->username : QString(); - int32 botStatus = _peer->isChat() ? _peer->asChat()->botStatus : (_peer->isChannel() ? _peer->asChannel()->botStatus : -1); + int32 botStatus = _peer->isChat() ? _peer->asChat()->botStatus : (_peer->isMegagroup() ? _peer->asChannel()->mgInfo->botStatus : -1); if (!replyTo && toSend.indexOf('@') < 2 && !username.isEmpty() && (botStatus == 0 || botStatus == 2)) { toSend += '@' + username; } @@ -4342,7 +4342,7 @@ void HistoryWidget::insertBotCommand(const QString &cmd) { PeerData *bot = _peer->isUser() ? _peer : (App::hoveredLinkItem() ? (App::hoveredLinkItem()->toHistoryForwarded() ? App::hoveredLinkItem()->toHistoryForwarded()->fromForwarded() : App::hoveredLinkItem()->from()) : 0); if (!bot->isUser() || !bot->asUser()->botInfo) bot = 0; QString username = bot ? bot->asUser()->username : QString(); - int32 botStatus = _peer->isChat() ? _peer->asChat()->botStatus : (_peer->isChannel() ? _peer->asChannel()->botStatus : -1); + int32 botStatus = _peer->isChat() ? _peer->asChat()->botStatus : (_peer->isMegagroup() ? _peer->asChannel()->mgInfo->botStatus : -1); if (toInsert.indexOf('@') < 2 && !username.isEmpty() && (botStatus == 0 || botStatus == 2)) { toInsert += '@' + username; } @@ -4444,16 +4444,7 @@ void HistoryWidget::updateDragAreas() { } bool HistoryWidget::canSendMessages(PeerData *peer) const { - if (peer) { - if (peer->isUser()) { - return peer->asUser()->access != UserNoAccess; - } else if (peer->isChat()) { - return peer->asChat()->canWrite(); - } else if (peer->isChannel()) { - return peer->asChannel()->amIn() && (peer->asChannel()->canPublish() || !peer->asChannel()->isBroadcast()); - } - } - return false; + return peer && peer->canWrite(); } bool HistoryWidget::readyToForward() const { @@ -4483,7 +4474,7 @@ bool HistoryWidget::isMuteUnmute() const { bool HistoryWidget::updateCmdStartShown() { bool cmdStartShown = false; - if (_history && _peer && ((_peer->isChat() && _peer->asChat()->botStatus > 0) || (_peer->isChannel() && _peer->asChannel()->botStatus > 0) || (_peer->isUser() && _peer->asUser()->botInfo))) { + if (_history && _peer && ((_peer->isChat() && _peer->asChat()->botStatus > 0) || (_peer->isMegagroup() && _peer->asChannel()->mgInfo->botStatus > 0) || (_peer->isUser() && _peer->asUser()->botInfo))) { if (!isBotStart() && !isBlocked() && !_keyboard.hasMarkup() && !_keyboard.forceReply()) { if (!_field.hasSendText()) { cmdStartShown = true; @@ -6347,7 +6338,7 @@ QRect HistoryWidget::historyRect() const { } void HistoryWidget::destroyData() { - showPeerHistory(0, 0); + showHistory(0, 0); } QStringList HistoryWidget::getMediasFromMime(const QMimeData *d) { diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 12cb3343b..4be2749ad 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -516,7 +516,7 @@ public: void fastShowAtEnd(History *h); void applyDraft(bool parseLinks = true); - void showPeerHistory(const PeerId &peer, MsgId showAtMsgId); + void showHistory(const PeerId &peer, MsgId showAtMsgId); void clearDelayedShowAt(); void clearAllLoadRequests(); diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp index ec938912a..fbb19cd45 100644 --- a/Telegram/SourceFiles/intro/introsignup.cpp +++ b/Telegram/SourceFiles/intro/introsignup.cpp @@ -88,7 +88,7 @@ void IntroSignup::mousePressEvent(QMouseEvent *e) { showError(lang(lng_bad_photo)); return; } - PhotoCropBox *box = new PhotoCropBox(img, 0); + PhotoCropBox *box = new PhotoCropBox(img, PeerId(0)); connect(box, SIGNAL(ready(const QImage &)), this, SLOT(onPhotoReady(const QImage &))); App::wnd()->showLayer(box); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 9491d81f0..c71064c44 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2314,7 +2314,7 @@ void MainWidget::showPeerHistory(quint64 peerId, qint32 showAtMsgId, bool back) history.show(); } if (history.peer() && history.peer()->id != peerId) clearBotStartToken(history.peer()); - history.showPeerHistory(peerId, showAtMsgId); + history.showHistory(peerId, showAtMsgId); bool noPeer = (!history.peer() || !history.peer()->id), onlyDialogs = noPeer && !cWideMode(); if (profile || overview) { @@ -2487,7 +2487,7 @@ void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool } history.animStop(); if (back) clearBotStartToken(history.peer()); - history.showPeerHistory(0, 0); + history.showHistory(0, 0); history.hide(); if (!cWideMode()) dialogs.hide(); @@ -2533,7 +2533,7 @@ void MainWidget::showPeerProfile(PeerData *peer, bool back, int32 lastScrollTop) profile->animShow(animCache, animTopBarCache, back, lastScrollTop); history.animStop(); if (back) clearBotStartToken(history.peer()); - history.showPeerHistory(0, 0); + history.showHistory(0, 0); history.hide(); orderWidgets(); @@ -3639,7 +3639,7 @@ void MainWidget::inviteCheckDone(QString hash, const MTPChatInvite &invite) { switch (invite.type()) { case mtpc_chatInvite: { const MTPDchatInvite &d(invite.c_chatInvite()); - ConfirmBox *box = new ConfirmBox((d.is_channel() ? lng_group_invite_want_join_channel : lng_group_invite_want_join)(lt_title, qs(d.vtitle)), lang(lng_group_invite_join)); + ConfirmBox *box = new ConfirmBox(((d.is_channel() && !d.is_megagroup()) ? lng_group_invite_want_join_channel : lng_group_invite_want_join)(lt_title, qs(d.vtitle)), lang(lng_group_invite_join)); _inviteHash = hash; connect(box, SIGNAL(confirmed()), this, SLOT(onInviteImport())); App::wnd()->showLayer(box); @@ -3954,7 +3954,7 @@ int32 MainWidget::dlgsWidth() const { } MainWidget::~MainWidget() { - if (App::main() == this) history.showPeerHistory(0, 0); + if (App::main() == this) history.showHistory(0, 0); delete _background; diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index f8ea8e98e..f3ef9a6e8 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -129,6 +129,9 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee if (chatPhoto && chatPhoto->date) { _photoLink = TextLinkPtr(new PhotoLink(chatPhoto, _peer)); } + if (_peerChannel->isMegagroup() && _peerChannel->mgInfo->lastParticipants.isEmpty()) { + if (App::api()) App::api()->requestLastParticipants(_peerChannel); + } _peerChannel->updateFull(); } @@ -294,7 +297,7 @@ void ProfileInner::onUpdatePhoto() { saveError(lang(lng_bad_photo)); return; } - PhotoCropBox *box = new PhotoCropBox(img, _peer->id); + PhotoCropBox *box = new PhotoCropBox(img, _peer); connect(box, SIGNAL(closed()), this, SLOT(onPhotoUpdateStart())); App::wnd()->showLayer(box); } @@ -369,9 +372,15 @@ bool ProfileInner::blockFail(const RPCError &error) { } void ProfileInner::onAddParticipant() { - if (!_peerChat) return; - - App::wnd()->showLayer(new ContactsBox(_peerChat, MembersFilterRecent)); + if (_peerChat) { + App::wnd()->showLayer(new ContactsBox(_peerChat, MembersFilterRecent)); + } else if (_peerChannel && _peerChannel->mgInfo) { + MembersAlreadyIn already; + for (MegagroupInfo::LastParticipants::const_iterator i = _peerChannel->mgInfo->lastParticipants.cbegin(), e = _peerChannel->mgInfo->lastParticipants.cend(); i != e; ++i) { + already.insert(*i, true); + } + App::wnd()->showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already)); + } } void ProfileInner::onMigrate() { @@ -669,6 +678,28 @@ void ProfileInner::reorderParticipants() { _onlineText = lng_chat_status_members(lt_count, _participants.size()); } loadProfilePhotos(_lastPreload); + } else if (_peerChannel && _peerChannel->isMegagroup() && _peerChannel->amIn() && !_peerChannel->mgInfo->lastParticipants.isEmpty()) { + if (!_peerChannel->mgInfo->lastParticipants.isEmpty()) { + _participants.clear(); + for (ParticipantsData::iterator i = _participantsData.begin(), e = _participantsData.end(); i != e; ++i) { + if (*i) { + delete *i; + *i = 0; + } + } + _participants.reserve(_peerChannel->mgInfo->lastParticipants.size()); + _participantsData.resize(_peerChannel->mgInfo->lastParticipants.size()); + } + UserData *self = App::self(); + bool onlyMe = true; + for (MegagroupInfo::LastParticipants::const_iterator i = _peerChannel->mgInfo->lastParticipants.cbegin(), e = _peerChannel->mgInfo->lastParticipants.cend(); i != e; ++i) { + _participants.push_back(*i); + } + if (_peerChannel->mgInfo->lastParticipants.isEmpty()) { + if (App::api()) App::api()->requestLastParticipants(_peerChannel); + } + _onlineText = (_peerChannel->count > 0) ? lng_chat_status_members(lt_count, _peerChannel->count) : lang(_peerChannel->isMegagroup() ? lng_group_status : lng_channel_status); + loadProfilePhotos(_lastPreload); } else { _participants.clear(); if (_peerUser) { @@ -744,10 +775,10 @@ void ProfileInner::paintEvent(QPaintEvent *e) { addbyname = st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent); p.setPen(st::black->p); p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + st::profileStatusTop + st::linkFont->ascent, '@' + _peerUser->username); - } else if (_peerChannel && (_peerChannel->isPublic() || _amCreator)) { + } else if (_peerChannel && !_peerChannel->isMegagroup() && (_peerChannel->isPublic() || _amCreator )) { addbyname = st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent); } - if (!_peerChannel || !_peerChannel->canViewParticipants()) { + if (!_peerChannel || !_peerChannel->canViewParticipants() || _peerChannel->isMegagroup()) { p.setPen((_peerUser && App::onlineColorUse(_peerUser, l_time) ? st::profileOnlineColor : st::profileOfflineColor)->p); p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop + st::linkFont->ascent, _onlineText); } @@ -855,21 +886,21 @@ void ProfileInner::paintEvent(QPaintEvent *e) { p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, lang(lng_profile_actions_section)); top += st::profileHeaderSkip; - top += _searchInPeer.height(); + top += _searchInPeer.height() + st::setLittleSkip; if (_peerUser || _peerChat) { - top += st::setLittleSkip + _clearHistory.height(); + top += _clearHistory.height() + st::setLittleSkip; } if (_peerUser || _peerChat || (_peerChannel->amIn() && !_amCreator)) { - top += st::setLittleSkip + _deleteConversation.height(); + top += _deleteConversation.height(); } if (_peerUser && peerToUser(_peerUser->id) != MTP::authedId()) { top += st::setSectionSkip + _blockUser.height(); } else if (_peerChannel && _amCreator) { - top += st::setSectionSkip + _deleteChannel.height(); + top += (_peerChannel->isMegagroup() ? 0 : (st::setSectionSkip - st::setLittleSkip)) + _deleteChannel.height(); } // participants - if (_peerChat && _peerChat->amIn()) { + if ((_peerChat && _peerChat->amIn()) || (_peerChannel && _peerChannel->isMegagroup() && _peerChannel->amIn())) { QString sectionHeader = lang(_participants.isEmpty() ? lng_profile_loading : lng_profile_participants_section); p.setFont(st::profileHeaderFont->f); p.setPen(st::profileHeaderColor->p); @@ -905,10 +936,12 @@ void ProfileInner::paintEvent(QPaintEvent *e) { } if (_amCreator) { data->cankick = (user != App::self()); - } else if (_peerChat->amAdmin()) { + } else if (_peerChat && _peerChat->amAdmin()) { data->cankick = (user != App::self()) && (_peerChat->admins.constFind(user) == _peerChat->admins.cend()) && (peerFromUser(_peerChat->creator) != user->id); + } else if (_peerChannel && _peerChannel->amEditor()) { + data->cankick = (user != App::self()) && (_peerChannel->mgInfo->lastAdmins.constFind(user) == _peerChannel->mgInfo->lastAdmins.cend()); } else { - data->cankick = (user != App::self()) && (_peerChat->invitedByMe.constFind(user) != _peerChat->invitedByMe.cend()); + data->cankick = (user != App::self()) && !_peerChannel && (_peerChat->invitedByMe.constFind(user) != _peerChat->invitedByMe.cend()); } } p.setPen(st::profileListNameColor->p); @@ -971,21 +1004,20 @@ void ProfileInner::updateSelected() { update(QRect(_left, _aboutTop, _width, _aboutHeight)); } - int32 partfrom = _searchInPeer.y() + _searchInPeer.height(); - if (_peerUser || _peerChat) { - partfrom = _clearHistory.y() + _clearHistory.height(); + int32 participantsTop = 0; + if (_peerChannel && _amCreator) { + participantsTop = _deleteChannel.y() + _deleteChannel.height(); + } else { + participantsTop = _deleteConversation.y() + _deleteConversation.height(); } - if (_peerUser || _peerChat || (_peerChannel->amIn() && !_amCreator)) { - partfrom = _deleteConversation.y() + _deleteConversation.height(); - } - partfrom += st::profileHeaderSkip; - int32 newSelected = (lp.x() >= _left - st::profileListPadding.width() && lp.x() < _left + _width + st::profileListPadding.width() && lp.y() >= partfrom) ? (lp.y() - partfrom) / _pHeight : -1; + participantsTop += st::profileHeaderSkip; + int32 newSelected = (lp.x() >= _left - st::profileListPadding.width() && lp.x() < _left + _width + st::profileListPadding.width() && lp.y() >= participantsTop) ? (lp.y() - participantsTop) / _pHeight : -1; UserData *newKickOver = 0; if (newSelected >= 0 && newSelected < _participants.size()) { ParticipantData *data = _participantsData[newSelected]; if (data && data->cankick) { - int32 top = partfrom + newSelected * _pHeight + st::profileListNameTop; + int32 top = participantsTop + newSelected * _pHeight + st::profileListNameTop; if ((lp.x() >= _left + _width - _kickWidth) && (lp.x() < _left + _width) && (lp.y() >= top) && (lp.y() < top + st::linkFont->height)) { newKickOver = _participants[newSelected]; } @@ -1056,9 +1088,12 @@ void ProfileInner::mouseReleaseEvent(QMouseEvent *e) { } void ProfileInner::onKickConfirm() { - if (!_peerChat) return; - - App::main()->kickParticipant(_peerChat, _kickConfirm); + if (_peerChat) { + App::main()->kickParticipant(_peerChat, _kickConfirm); + } else if (_peerChannel) { + App::wnd()->hideLayer(); + App::api()->kickParticipant(_peerChannel, _kickConfirm); + } } void ProfileInner::keyPressEvent(QKeyEvent *e) { @@ -1299,12 +1334,12 @@ void ProfileInner::resizeEvent(QResizeEvent *e) { top += st::setSectionSkip; _blockUser.move(_left, top); top += _blockUser.height(); } else if (_peerChannel && _amCreator) { - top += st::setSectionSkip; + top += (_peerChannel->isMegagroup() ? 0 : (st::setSectionSkip - st::setLittleSkip)); _deleteChannel.move(_left, top); top += _deleteChannel.height(); } // participants - if (_peerChat && _peerChat->amIn()) { + if ((_peerChat && _peerChat->amIn()) || (_peerChannel && _peerChannel->isMegagroup() && _peerChannel->amIn())) { top += st::profileHeaderSkip; if (!_participants.isEmpty()) { int32 fullCnt = _participants.size(); @@ -1427,6 +1462,13 @@ int32 ProfileInner::countMinHeight() { } else { h = _searchInPeer.y() + _searchInPeer.height() + st::profileHeaderSkip; } + if (_peerChannel->isMegagroup()) { + if (!_participants.isEmpty()) { + h += st::profileHeaderSkip + _participants.size() * _pHeight; + } else if (_peerChannel->amIn()) { + h += st::profileHeaderSkip; + } + } } return h; } @@ -1555,7 +1597,11 @@ void ProfileInner::showAll() { _invitationLink.hide(); } } - _addParticipant.hide(); + if (_peerChannel->count < cMaxMegaGroupCount() && _peerChannel->isMegagroup() && (_amCreator || _peerChannel->amEditor())) { + _addParticipant.show(); + } else { + _addParticipant.hide(); + } _blockUser.hide(); if (_amCreator) { _deleteChannel.show(); @@ -1572,7 +1618,7 @@ void ProfileInner::showAll() { } else { _admins.hide(); } - if (_peerChannel->canViewParticipants()) { + if (_peerChannel->canViewParticipants() && !_peerChannel->isMegagroup()) { _members.show(); } else { _members.hide(); diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp index 853c9845e..4b3b8264a 100644 --- a/Telegram/SourceFiles/settingswidget.cpp +++ b/Telegram/SourceFiles/settingswidget.cpp @@ -1169,7 +1169,7 @@ void SettingsInner::onUpdatePhoto() { saveError(lang(lng_bad_photo)); return; } - PhotoCropBox *box = new PhotoCropBox(img, self()->id); + PhotoCropBox *box = new PhotoCropBox(img, self()); connect(box, SIGNAL(closed()), this, SLOT(onPhotoUpdateStart())); App::wnd()->showLayer(box); } diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index fbb2390ea..5c4490567 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -1026,6 +1026,7 @@ void CommentsLink::onClick(Qt::MouseButton button) const { } MsgId clientMsgId() { - static MsgId current = -2000000000; - return ++current; + static MsgId currentClientMsgId = StartClientMsgId; + Q_ASSERT(currentClientMsgId < EndClientMsgId); + return currentClientMsgId++; } diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 6ef82fade..97d639641 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -142,8 +142,14 @@ inline bool operator<(const FullMsgId &a, const FullMsgId &b) { return a.channel < b.channel; } +static const MsgId StartClientMsgId = -0x7FFFFFFF; +static const MsgId EndClientMsgId = -0x40000000; +inline bool isClientMsgId(MsgId id) { + return id >= StartClientMsgId && id < EndClientMsgId; +} static const MsgId ShowAtTheEndMsgId = -0x40000000; static const MsgId SwitchAtTopMsgId = -0x3FFFFFFF; +static const MsgId ServerMaxMsgId = 0x3FFFFFFF; static const MsgId ShowAtUnreadMsgId = 0; struct NotifySettings { @@ -212,6 +218,7 @@ public: } bool isVerified() const; bool isMegagroup() const; + bool canWrite() const; UserData *asUser(); const UserData *asUser() const; ChatData *asChat(); @@ -350,6 +357,9 @@ public: bool isVerified() const { return flags & MTPDuser::flag_verified; } + bool canWrite() const { + return access != UserNoAccess; + } MTPInputUser inputUser; @@ -373,7 +383,7 @@ public: class ChatData : public PeerData { public: - ChatData(const PeerId &id) : PeerData(id), inputChat(MTP_int(bareId())), count(0), date(0), version(0), creator(0), inviterForSpamReport(0), flags(0), isForbidden(false), botStatus(0) { + ChatData(const PeerId &id) : PeerData(id), inputChat(MTP_int(bareId())), migrateTo(0), count(0), date(0), version(0), creator(0), inviterForSpamReport(0), flags(0), isForbidden(false), botStatus(0) { } void setPhoto(const MTPChatPhoto &photo, const PhotoId &phId = UnknownPeerPhotoId); void invalidateParticipants() { @@ -389,6 +399,8 @@ public: MTPint inputChat; + ChannelData *migrateTo; + int32 count; int32 date; int32 version; @@ -504,19 +516,24 @@ private: }; struct MegagroupInfo { - MegagroupInfo() : botStatus(-1) { + MegagroupInfo() : botStatus(-1), migrateFrom(0) { } typedef QList LastParticipants; LastParticipants lastParticipants; + typedef QMap LastAdmins; + LastAdmins lastAdmins; typedef QMap MarkupSenders; MarkupSenders markupSenders; + typedef QMap Bots; + Bots bots; int32 botStatus; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other + ChatData *migrateFrom; }; class ChannelData : public PeerData { public: - ChannelData(const PeerId &id) : PeerData(id), access(0), inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))), count(1), adminsCount(1), date(0), version(0), flags(0), flagsFull(0), mgInfo(0), isForbidden(true), botStatus(-1), inviter(0), _lastFullUpdate(0) { + ChannelData(const PeerId &id) : PeerData(id), access(0), inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))), count(1), adminsCount(1), date(0), version(0), flags(0), flagsFull(0), mgInfo(0), isForbidden(true), inviter(0), _lastFullUpdate(0) { setName(QString(), QString()); } void setPhoto(const MTPChatPhoto &photo, const PhotoId &phId = UnknownPeerPhotoId); @@ -566,6 +583,9 @@ public: bool canPublish() const { return amCreator() || amEditor(); } + bool canWrite() const { + return amIn() && (canPublish() || !isBroadcast()); + } bool canViewParticipants() const { return flagsFull & MTPDchannelFull::flag_can_view_participants; } @@ -574,7 +594,6 @@ public: return flags & MTPDchannel::flag_verified; } - int32 botStatus; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other // ImagePtr photoFull; QString invitationUrl; @@ -655,6 +674,9 @@ inline bool PeerData::isVerified() const { inline bool PeerData::isMegagroup() const { return isChannel() ? asChannel()->isMegagroup() : false; } +inline bool PeerData::canWrite() const { + return isChannel() ? asChannel()->canWrite() : (isChat() ? asChat()->canWrite() : (isUser() ? asUser()->canWrite() : false)); +} inline int32 newMessageFlags(PeerData *p) { return p->isSelf() ? 0 : (((p->isChat() || (p->isUser() && !p->asUser()->botInfo)) ? MTPDmessage::flag_unread : 0) | MTPDmessage::flag_out);