From 497602f47ef0ba1878b824fd8c5cbebddcd2765f Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 13 Nov 2015 18:14:33 +0300 Subject: [PATCH] merged supergroup with migrated group history, loading messages by 3 (temp! testing) --- Telegram/Resources/lang.strings | 6 +- Telegram/SourceFiles/apiwrap.cpp | 37 +- Telegram/SourceFiles/apiwrap.h | 2 + Telegram/SourceFiles/app.cpp | 148 ++- Telegram/SourceFiles/app.h | 32 +- Telegram/SourceFiles/boxes/addcontactbox.cpp | 6 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 38 +- Telegram/SourceFiles/boxes/contactsbox.h | 2 +- Telegram/SourceFiles/config.h | 4 +- Telegram/SourceFiles/dialogswidget.cpp | 44 +- Telegram/SourceFiles/dialogswidget.h | 4 +- Telegram/SourceFiles/gui/animation.cpp | 2 +- Telegram/SourceFiles/history.cpp | 161 ++- Telegram/SourceFiles/history.h | 16 +- Telegram/SourceFiles/historywidget.cpp | 1133 ++++++++++++------ Telegram/SourceFiles/historywidget.h | 44 +- Telegram/SourceFiles/localstorage.cpp | 2 +- Telegram/SourceFiles/mainwidget.cpp | 104 +- Telegram/SourceFiles/mainwidget.h | 12 +- Telegram/SourceFiles/mtproto/mtpCoreTypes.h | 1 + Telegram/SourceFiles/mtproto/mtpScheme.cpp | 47 +- Telegram/SourceFiles/mtproto/mtpScheme.h | 141 ++- Telegram/SourceFiles/mtproto/scheme.tl | 8 +- Telegram/SourceFiles/overviewwidget.cpp | 87 +- Telegram/SourceFiles/overviewwidget.h | 4 +- Telegram/SourceFiles/profilewidget.cpp | 7 +- Telegram/SourceFiles/structs.cpp | 18 + Telegram/SourceFiles/structs.h | 20 +- 28 files changed, 1390 insertions(+), 740 deletions(-) diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index 7637b2b29..5f57b7b25 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -484,6 +484,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org "lng_media_unsupported" = "Media Unsupported"; "lng_action_add_user" = "{from} added {user}"; +"lng_action_add_user_and_user" = "{from} added {user} and {second_user}"; +"lng_action_add_users" = "{from} added {user} and {count:_not_used|# more member|# more members}"; "lng_action_add_you" = "{from} added you to this channel"; "lng_action_you_joined" = "You joined this channel"; "lng_action_add_you_group" = "{from} added you to this group"; @@ -501,8 +503,6 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org "lng_action_changed_title_channel" = "Channel name was changed to «{title}»"; "lng_action_created_chat" = "{from} created group «{title}»"; "lng_action_created_channel" = "Channel «{title}» created"; -"lng_action_group_deactivate" = "The group was deactivated"; -"lng_action_group_activate" = "The group was activated"; "lng_action_group_migrate" = "The group was converted to a supergroup"; "lng_profile_migrate_reached" = "{count:_not_used_|# member|# members} limit reached"; @@ -716,6 +716,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org "lng_forwarding_from_two" = "{user} and {second_user}"; "lng_share_cant" = "Sorry, no way to share here :("; +"lng_reply_cant" = "Sorry, no way to reply to an old message in supergroup :("; +"lng_reply_cant_forward" = "Sorry, no way to reply to an old message in supergroup :( Do you wish to forward it and add your comment?"; "lng_contact_phone" = "Phone number"; "lng_enter_contact_data" = "New Contact"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 0433bcd7b..02c32ab51 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -286,10 +286,31 @@ 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; + if (f.has_migrated_from_chat_id()) { + if (!channel->mgInfo) { + channel->flags |= MTPDchannel::flag_megagroup; + channel->flagsUpdated(); + } + ChatData *cfrom = App::chat(peerFromChat(f.vmigrated_from_chat_id)); + bool updated = (cfrom->migrateToPtr != channel); + if (updated) { + cfrom->migrateToPtr = channel; + } + if (channel->mgInfo->migrateFromPtr != cfrom) { + channel->mgInfo->migrateFromPtr = cfrom; + if (History *h = App::historyLoaded(cfrom->id)) { + if (History *hto = App::historyLoaded(channel->id)) { + if (!h->isEmpty()) { + h->clear(true); + } + if (!hto->dialogs.isEmpty() && !h->dialogs.isEmpty()) { + App::removeDialog(h); + } + } + } + } + if (updated) { + App::main()->peerUpdated(cfrom); } } channel->about = qs(f.vabout); @@ -308,7 +329,7 @@ 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) { + if (channel->migrateFrom()) { h->asChannelHistory()->removeJoinedMessage(); } } @@ -797,6 +818,12 @@ void ApiWrap::resolveWebPages() { if (m < INT_MAX) _webPagesTimer.start(m * 1000); } +void ApiWrap::delayedRequestParticipantsCount() { + if (App::main() && App::main()->peer() && App::main()->peer()->isChannel()) { + requestFullPeer(App::main()->peer()); + } +} + void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) { const QVector *v = 0; switch (msgs.type()) { diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 1f2a6be95..2e3a0d6ad 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -62,6 +62,8 @@ public slots: void resolveReplyTo(); void resolveWebPages(); + void delayedRequestParticipantsCount(); + private: void gotReplyTo(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req); diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 8c8a69d23..a0c495014 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -185,11 +185,6 @@ namespace App { return main() ? main()->api() : 0; } - void showSettings() { - Window *w(wnd()); - if (w) w->showSettings(); - } - bool loggedOut() { Window *w(wnd()); if (cHasPasscode()) { @@ -503,6 +498,36 @@ namespace App { cdata->setPhoto(d.vphoto); cdata->date = d.vdate.v; + if (d.has_migrated_to() && d.vmigrated_to.type() == mtpc_inputChannel) { + const MTPDinputChannel &c(d.vmigrated_to.c_inputChannel()); + ChannelData *channel = App::channel(peerFromChannel(c.vchannel_id)); + if (!channel->mgInfo) { + channel->flags |= MTPDchannel::flag_megagroup; + channel->flagsUpdated(); + } + if (!channel->access) { + channel->input = MTP_inputPeerChannel(c.vchannel_id, c.vaccess_hash); + channel->inputChannel = d.vmigrated_to; + channel->access = d.vmigrated_to.c_inputChannel().vaccess_hash.v; + } + if (cdata->migrateToPtr != channel) { + cdata->migrateToPtr = channel; + } + if (channel->mgInfo->migrateFromPtr != cdata) { + channel->mgInfo->migrateFromPtr = cdata; + if (History *h = App::historyLoaded(cdata->id)) { + if (History *hto = App::historyLoaded(channel->id)) { + if (!h->isEmpty()) { + h->clear(true); + } + if (!hto->dialogs.isEmpty() && !h->dialogs.isEmpty()) { + App::removeDialog(h); + } + } + } + } + } + if (!(cdata->flags & MTPDchat::flag_admins_enabled) && (d.vflags.v & MTPDchat::flag_admins_enabled)) { cdata->invalidateParticipants(); } @@ -549,21 +574,7 @@ namespace App { cdata->date = d.vdate.v; cdata->flags = d.vflags.v; cdata->isForbidden = false; - if (cdata->isMegagroup()) { - if (!cdata->mgInfo) { - cdata->mgInfo = new MegagroupInfo(); - } - if (History *h = App::historyLoaded(cdata->id)) { - if (h->asChannelHistory()->onlyImportant()) { - MsgId fixInScrollMsgId = 0; - int32 fixInScrollMsgTop = 0; - h->asChannelHistory()->getSwitchReadyFor(SwitchAtTopMsgId, fixInScrollMsgId, fixInScrollMsgTop); - } - } - } else if (cdata->mgInfo) { - delete cdata->mgInfo; - cdata->mgInfo = 0; - } + cdata->flagsUpdated(); if (cdata->version < d.vversion.v) { cdata->version = d.vversion.v; } @@ -1025,7 +1036,7 @@ namespace App { MsgsData::const_iterator j = data->constFind(i->v); if (j != data->cend()) { History *h = (*j)->history(); - if (App::main() && h->peer == App::main()->peer() && !(*j)->detached()) { + if (App::main() && (h->peer == App::main()->peer() || (App::main()->peer() && h->peer->migrateTo() == App::main()->peer())) && !(*j)->detached()) { resized = true; } (*j)->destroy(); @@ -2533,48 +2544,6 @@ namespace App { } } - void sendBotCommand(const QString &cmd, MsgId replyTo) { - if (App::main()) { - App::main()->sendBotCommand(cmd, replyTo); - } - } - - void insertBotCommand(const QString &cmd) { - if (App::main()) { - App::main()->insertBotCommand(cmd); - } - } - - void searchByHashtag(const QString &tag, PeerData *inPeer) { - if (App::main()) { - App::main()->searchMessages(tag + ' ', (inPeer && inPeer->isChannel()) ? inPeer : 0); - } - } - - void openPeerByName(const QString &username, bool toProfile, const QString &startToken) { - if (App::main()) { - App::main()->openPeerByName(username, toProfile, startToken); - } - } - - void joinGroupByHash(const QString &hash) { - if (App::main()) { - App::main()->joinGroupByHash(hash); - } - } - - void stickersBox(const QString &name) { - if (App::main()) { - App::main()->stickersBox(MTP_inputStickerSetShortName(MTP_string(name))); - } - } - - void openLocalUrl(const QString &url) { - if (App::main()) { - App::main()->openLocalUrl(url); - } - } - QImage **cornersMask() { return ::cornersMask; } @@ -2821,4 +2790,57 @@ namespace App { WallPapers gServerBackgrounds; + void sendBotCommand(const QString &cmd, MsgId replyTo) { + if (MainWidget *m = main()) m->sendBotCommand(cmd, replyTo); + } + + void insertBotCommand(const QString &cmd) { + if (MainWidget *m = main()) m->insertBotCommand(cmd); + } + + void searchByHashtag(const QString &tag, PeerData *inPeer) { + if (MainWidget *m = main()) m->searchMessages(tag + ' ', (inPeer && inPeer->isChannel()) ? inPeer : 0); + } + + void openPeerByName(const QString &username, bool toProfile, const QString &startToken) { + if (MainWidget *m = main()) m->openPeerByName(username, toProfile, startToken); + } + + void joinGroupByHash(const QString &hash) { + if (MainWidget *m = main()) m->joinGroupByHash(hash); + } + + void stickersBox(const QString &name) { + if (MainWidget *m = main()) m->stickersBox(MTP_inputStickerSetShortName(MTP_string(name))); + } + + void openLocalUrl(const QString &url) { + if (MainWidget *m = main()) m->openLocalUrl(url); + } + + bool forward(const PeerId &peer, ForwardWhatMessages what) { + if (MainWidget *m = main()) return m->onForward(peer, what); + return false; + } + + void removeDialog(History *history) { + if (MainWidget *m = main()) m->removeDialog(history); + } + + void showSettings() { + if (Window *win = wnd()) win->showSettings(); + } + + void showLayer(LayeredWidget *w, bool forceFast) { + if (Window *win = wnd()) win->showLayer(w, forceFast); + } + + void replaceLayer(LayeredWidget *w) { + if (Window *win = wnd()) win->replaceLayer(w); + } + + void showLayerLast(LayeredWidget *w) { + if (Window *win = wnd()) win->showLayerLast(w); + } + } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 615be17a3..6e22bd816 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -80,6 +80,15 @@ enum RoundCorners { RoundCornersCount }; +enum ForwardWhatMessages { + ForwardSelectedMessages, + ForwardContextMessage, + ForwardPressedMessage, + ForwardPressedLinkMessage +}; + +class LayeredWidget; + namespace App { Application *app(); Window *wnd(); @@ -89,7 +98,6 @@ namespace App { FileUploader *uploader(); ApiWrap *api(); - void showSettings(); void logOut(); bool loggedOut(); @@ -257,14 +265,6 @@ namespace App { void setProxySettings(QNetworkAccessManager &manager); void setProxySettings(QTcpSocket &socket); - void sendBotCommand(const QString &cmd, MsgId replyTo = 0); - void insertBotCommand(const QString &cmd); - void searchByHashtag(const QString &tag, PeerData *inPeer); - void openPeerByName(const QString &username, bool toProfile = false, const QString &startToken = QString()); - void joinGroupByHash(const QString &hash); - void stickersBox(const QString &name); - void openLocalUrl(const QString &url); - QImage **cornersMask(); void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh = 0); inline void roundRect(Painter &p, const QRect &rect, const style::color &bg, RoundCorners index, const style::color *sh = 0) { @@ -299,4 +299,18 @@ namespace App { typedef QList WallPapers; DeclareSetting(WallPapers, ServerBackgrounds); + void sendBotCommand(const QString &cmd, MsgId replyTo = 0); + void insertBotCommand(const QString &cmd); + void searchByHashtag(const QString &tag, PeerData *inPeer); + void openPeerByName(const QString &username, bool toProfile = false, const QString &startToken = QString()); + void joinGroupByHash(const QString &hash); + void stickersBox(const QString &name); + void openLocalUrl(const QString &url); + bool forward(const PeerId &peer, ForwardWhatMessages what); + void removeDialog(History *history); + void showSettings(); + void showLayer(LayeredWidget *w, bool forceFast = false); + void replaceLayer(LayeredWidget *w); + void showLayerLast(LayeredWidget *w); + }; diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index def85ee2c..05fe432e5 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -1195,7 +1195,11 @@ void EditChannelBox::showAll() { _description.show(); _save.show(); _cancel.show(); - _publicLink.show(); + if (_channel->isMegagroup()) { + _publicLink.hide(); + } else { + _publicLink.show(); + } } void EditChannelBox::showDone() { diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 9819e58c3..2be113897 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -43,7 +43,7 @@ ContactsInner::ContactsInner(CreatingGroupType creating) : TWidget() , _bot(0) , _creating(creating) , _allAdmins(this, lang(lng_chat_all_members_admins), false, st::contactsAdminCheckbox) -, _addToChat(0) +, _addToPeer(0) , _addAdmin(0) , _addAdminRequestId(0) , _addAdminBox(0) @@ -70,7 +70,7 @@ ContactsInner::ContactsInner(ChannelData *channel, MembersFilter membersFilter, , _creating(CreatingGroupChannel) , _already(already) , _allAdmins(this, lang(lng_chat_all_members_admins), false, st::contactsAdminCheckbox) -, _addToChat(0) +, _addToPeer(0) , _addAdmin(0) , _addAdminRequestId(0) , _addAdminBox(0) @@ -105,7 +105,7 @@ ContactsInner::ContactsInner(ChatData *chat, MembersFilter membersFilter) : TWid , _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right() - st::contactsCheckPosition.x() * 2 - st::contactsCheckIcon.pxWidth()) , _aboutAllAdmins(st::boxTextFont, lang(lng_chat_about_all_admins), _defaultOptions, _aboutWidth) , _aboutAdmins(st::boxTextFont, lang(lng_chat_about_admins), _defaultOptions, _aboutWidth) -, _addToChat(0) +, _addToPeer(0) , _addAdmin(0) , _addAdminRequestId(0) , _addAdminBox(0) @@ -135,7 +135,7 @@ ContactsInner::ContactsInner(UserData *bot) : TWidget() , _bot(bot) , _creating(CreatingGroupNone) , _allAdmins(this, lang(lng_chat_all_members_admins), false, st::contactsAdminCheckbox) -, _addToChat(0) +, _addToPeer(0) , _addAdmin(0) , _addAdminRequestId(0) , _addAdminBox(0) @@ -152,6 +152,8 @@ ContactsInner::ContactsInner(UserData *bot) : TWidget() for (DialogRow *r = v.list.begin; r != v.list.end; r = r->next) { if (r->history->peer->isChat() && r->history->peer->asChat()->canEdit()) { _contacts->addToEnd(r->history); + } else if (r->history->peer->isMegagroup() && (r->history->peer->asChannel()->amCreator() || r->history->peer->asChannel()->amEditor())) { + _contacts->addToEnd(r->history); } } init(); @@ -219,12 +221,12 @@ void ContactsInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &old void ContactsInner::onAddBot() { if (_bot->botInfo && !_bot->botInfo->startGroupToken.isEmpty()) { - MTP::send(MTPmessages_StartBot(_bot->inputUser, _addToChat->inputChat, MTP_long(MTP::nonce()), MTP_string(_bot->botInfo->startGroupToken)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _bot)); + MTP::send(MTPmessages_StartBot(_bot->inputUser, _addToPeer->input, MTP_long(MTP::nonce()), MTP_string(_bot->botInfo->startGroupToken)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _bot)); } else { - App::main()->addParticipants(_addToChat, QVector(1, _bot)); + App::main()->addParticipants(_addToPeer, QVector(1, _bot)); } App::wnd()->hideLayer(); - App::main()->showPeerHistory(_addToChat->id, ShowAtUnreadMsgId); + App::main()->showPeerHistory(_addToPeer->id, ShowAtUnreadMsgId); } void ContactsInner::onAddAdmin() { @@ -734,8 +736,8 @@ void ContactsInner::chooseParticipant() { connect(_addAdminBox, SIGNAL(confirmed()), this, SLOT(onAddAdmin())); connect(_addAdminBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoAddAdminBox(QObject*))); App::wnd()->replaceLayer(_addAdminBox); - } else if (bot() && peer->isChat()) { - _addToChat = peer->asChat(); + } else if (bot() && (peer->isChat() || peer->isMegagroup())) { + _addToPeer = peer; ConfirmBox *box = new ConfirmBox(lng_bot_sure_invite(lt_group, peer->name)); connect(box, SIGNAL(confirmed()), this, SLOT(onAddBot())); App::wnd()->replaceLayer(box); @@ -982,9 +984,21 @@ void ContactsInner::peopleReceived(const QString &query, const QVector PeerData *p = App::peer(peerId); if (!p) continue; - if ((!p->isUser() || (p->asUser()->botInfo && p->asUser()->botInfo->cantJoinGroups)) && (_chat || _creating == CreatingGroupGroup)) continue; // skip bot's that can't be invited to groups - if (_channel && !p->isUser()) continue; - if (p->isUser() && p->asUser()->botInfo && _channel && _membersFilter != MembersFilterAdmins) continue; // skip bots in channels + if (_channel || _chat || _creating != CreatingGroupNone) { + if (p->isUser()) { + if (p->asUser()->botInfo) { + if (_chat || _creating == CreatingGroupGroup) { // skip bot's that can't be invited to groups + if (p->asUser()->botInfo->cantJoinGroups) continue; + } + if (_channel) { + if (_channel->isMegagroup() && _membersFilter == MembersFilterAdmins) continue; + if (!_channel->isMegagroup() && _membersFilter != MembersFilterAdmins) continue; + } + } + } else { + continue; // skip + } + } ContactData *d = new ContactData(); _byUsernameDatas.push_back(d); diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index a513bb1f2..3e22c4726 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -132,7 +132,7 @@ private: int32 _aboutWidth; Text _aboutAllAdmins, _aboutAdmins; - ChatData *_addToChat; + PeerData *_addToPeer; UserData *_addAdmin; mtpRequestId _addAdminRequestId; ConfirmBox *_addAdminBox; diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index e8aa36624..0bd1e2cd7 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -309,8 +309,8 @@ enum { DialogsFirstLoad = 20, // first dialogs part size requested DialogsPerPage = 500, // next dialogs part size - MessagesFirstLoad = 30, // first history part size requested - MessagesPerPage = 50, // next history part size + MessagesFirstLoad = 3, // first history part size requested + MessagesPerPage = 3, // next history part size FileLoaderQueueStopTimeout = 5000, diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 00dca7e94..eab2c235b 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -444,22 +444,22 @@ void DialogsInner::createDialog(History *history) { } } -void DialogsInner::removePeer(PeerData *peer) { - if (sel && sel->history->peer == peer) { +void DialogsInner::removeDialog(History *history) { + if (!history) return; + if (sel && sel->history == history) { sel = 0; } - History *history = App::history(peer->id); - dialogs.del(peer); + dialogs.del(history->peer); history->dialogs = History::DialogLinks(); history->clearNotifications(); if (App::wnd()) App::wnd()->notifyClear(history); - if (contacts.list.rowByPeer.constFind(peer->id) != contacts.list.rowByPeer.cend()) { - if (contactsNoDialogs.list.rowByPeer.constFind(peer->id) == contactsNoDialogs.list.rowByPeer.cend()) { - contactsNoDialogs.addByName(App::history(peer->id)); + if (contacts.list.rowByPeer.constFind(history->peer->id) != contacts.list.rowByPeer.cend()) { + if (contactsNoDialogs.list.rowByPeer.constFind(history->peer->id) == contactsNoDialogs.list.rowByPeer.cend()) { + contactsNoDialogs.addByName(history); } } - Local::removeSavedPeer(peer); + Local::removeSavedPeer(history->peer); emit App::main()->dialogsUpdated(); @@ -811,10 +811,15 @@ void DialogsInner::dialogsReceived(const QVector &added) { } if (history) { - if (!history->lastMsgDate.isNull()) addSavedPeersAfter(history->lastMsgDate); - History::DialogLinks links = dialogs.addToEnd(history); - history->dialogs = links; + if (!history->lastMsgDate.isNull()) { + addSavedPeersAfter(history->lastMsgDate); + } contactsNoDialogs.del(history->peer); + if (history->peer->migrateFrom()) { + removeDialog(App::historyLoaded(history->peer->migrateFrom()->id)); + } else if (history->peer->migrateTo() && history->peer->migrateTo()->amIn()) { + removeDialog(history); + } } } @@ -1551,7 +1556,15 @@ void DialogsWidget::activate() { } void DialogsWidget::createDialog(History *history) { + bool creating = history->dialogs.isEmpty(); _inner.createDialog(history); + if (creating && history->peer->migrateFrom()) { + if (History *h = App::historyLoaded(history->peer->migrateFrom()->id)) { + if (!h->dialogs.isEmpty()) { + removeDialog(h); + } + } + } } void DialogsWidget::dlgUpdated(DialogRow *row) { @@ -2072,7 +2085,7 @@ void DialogsWidget::onFilterUpdate(bool force) { if (_a_show.animating() && !force) return; QString filterText = _filter.getLastText(); - _inner.onFilterUpdate(filterText); + _inner.onFilterUpdate(filterText, force); if (filterText.isEmpty()) { _searchCache.clear(); _searchQueries.clear(); @@ -2095,7 +2108,6 @@ void DialogsWidget::searchInPeer(PeerData *peer) { _searchInPeer = peer; _inner.searchInPeer(peer); onFilterUpdate(true); - _inner.onFilterUpdate(_filter.getLastText(), true); } void DialogsWidget::onFilterCursorMoved(int from, int to) { @@ -2234,11 +2246,9 @@ void DialogsWidget::scrollToPeer(const PeerId &peer, MsgId msgId) { _inner.scrollToPeer(peer, msgId); } -void DialogsWidget::removePeer(PeerData *peer) { - _filter.setText(QString()); - _filter.updatePlaceholder(); +void DialogsWidget::removeDialog(History *history) { + _inner.removeDialog(history); onFilterUpdate(); - _inner.removePeer(peer); } void DialogsWidget::removeContact(UserData *user) { diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 0aa9c8dee..6b4034271 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -61,7 +61,7 @@ public: void moveDialogToTop(const History::DialogLinks &links); void dlgUpdated(DialogRow *row); void dlgUpdated(History *row, MsgId msgId); - void removePeer(PeerData *peer); + void removeDialog(History *history); void removeContact(UserData *user); void loadPeerPhotos(int32 yFrom); @@ -216,7 +216,7 @@ public: void peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) const; void scrollToPeer(const PeerId &peer, MsgId msgId); - void removePeer(PeerData *peer); + void removeDialog(History *history); void removeContact(UserData *user); DialogsIndexed &contactsList(); diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index b05b16b0d..8cd76f8b0 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -153,7 +153,7 @@ bool AnimatedGif::animStep(float64 ms) { if (frame != f) { frame = f; if (msg && App::main()) { - App::main()->msgUpdated(msg->history()->peer->id, msg); + App::main()->msgUpdated(msg); } else { emit updated(); } diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 11cfd3359..b612be201 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -239,6 +239,11 @@ void DialogRow::paint(Painter &p, int32 w, bool act, bool sel, bool onlyBackgrou // draw unread int32 lastWidth = namewidth, unread = history->unreadCount; + if (history->peer->migrateFrom()) { + if (History *h = App::historyLoaded(history->peer->migrateFrom()->id)) { + unread += h->unreadCount; + } + } if (unread) { QString unreadStr = QString::number(unread); int32 unreadWidth = st::dlgUnreadFont->width(unreadStr); @@ -322,7 +327,7 @@ void FakeDialogRow::paint(Painter &p, int32 w, bool act, bool sel, bool onlyBack } // draw unread - int32 lastWidth = namewidth, unread = history->unreadCount; + int32 lastWidth = namewidth; _item->drawInDialog(p, QRect(nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, lastWidth, st::dlgFont->height), act, _cacheFor, _cache); p.setPen((act ? st::dlgActiveColor : st::dlgNameColor)->p); @@ -684,7 +689,7 @@ void ChannelHistory::addNewGroup(const MTPMessageGroup &group) { } HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) { - if (_joinedMessage || !peer->asChannel()->amIn() || (peer->asChannel()->mgInfo && peer->asChannel()->mgInfo->migrateFrom)) { + if (_joinedMessage || !peer->asChannel()->amIn() || peer->asChannel()->migrateFrom()) { return _joinedMessage; } @@ -811,7 +816,7 @@ HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) { } void ChannelHistory::checkJoinedMessage(bool createUnread) { - if (_joinedMessage || peer->asChannel()->inviter <= 0 || (peer->asChannel()->mgInfo && peer->asChannel()->mgInfo->migrateFrom)) { + if (_joinedMessage || peer->asChannel()->inviter <= 0 || peer->asChannel()->migrateFrom()) { return; } if (isEmpty()) { @@ -1529,14 +1534,6 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo if (chat) chat->updateName(qs(d.vtitle), QString(), QString()); } break; - case mtpc_messageActionChatDeactivate: { - peer->asChat()->flags |= MTPDchat::flag_deactivated; - } break; - - case mtpc_messageActionChatActivate: { - peer->asChat()->flags &= ~MTPDchat::flag_deactivated; - } break; - case mtpc_messageActionChatMigrateTo: { peer->asChat()->flags |= MTPDchat::flag_deactivated; @@ -1592,7 +1589,7 @@ HistoryItem *History::addNewMessage(const MTPMessage &msg, NewMessageType type) if (isChannel()) return asChannelHistory()->addNewChannelMessage(msg, type); if (type == NewMessageExisting) return addToHistory(msg); - if (!loadedAtBottom()) { + if (!loadedAtBottom() || peer->migrateTo()) { HistoryItem *item = addToHistory(msg); if (item) { setLastMessage(item); @@ -1814,7 +1811,7 @@ void History::newItemAdded(HistoryItem *item) { notifies.push_back(item); App::main()->newUnreadMsg(this, item); } - } else { + } else if (!item->isGroupMigrate() || !peer->isMegagroup()) { inboxRead(item); } } @@ -2217,7 +2214,18 @@ MsgId History::inboxRead(MsgId upTo) { if (!upTo) upTo = msgIdForRead(); inboxReadBefore = qMax(inboxReadBefore, upTo + 1); - if (!dialogs.isEmpty() && App::main()) App::main()->dlgUpdated(dialogs[0]); + if (App::main()) { + if (!dialogs.isEmpty()) { + App::main()->dlgUpdated(dialogs[0]); + } + if (peer->migrateTo()) { + if (History *h = App::historyLoaded(peer->migrateTo()->id)) { + if (!h->dialogs.isEmpty()) { + App::main()->dlgUpdated(h->dialogs[0]); + } + } + } + } showFrom = 0; App::wnd()->notifyClear(this); @@ -2270,7 +2278,15 @@ void History::setUnreadCount(int32 newUnreadCount, bool psUpdate) { if (mute) App::histories().unreadMuted += newUnreadCount - unreadCount; unreadCount = newUnreadCount; if (psUpdate && (!mute || cIncludeMuted())) App::wnd()->updateCounter(); - if (unreadBar) unreadBar->setCount(unreadCount); + if (unreadBar) { + int32 count = unreadCount; + if (peer->migrateTo()) { + if (History *h = App::historyLoaded(peer->migrateTo()->id)) { + count += h->unreadCount; + } + } + unreadBar->setCount(count); + } } } @@ -2312,8 +2328,14 @@ void History::getNextShowFrom(HistoryBlock *block, int32 i) { void History::addUnreadBar() { if (unreadBar || !showFrom || showFrom->detached() || !unreadCount) return; + int32 count = unreadCount; + if (peer->migrateTo()) { + if (History *h = App::historyLoaded(peer->migrateTo()->id)) { + count += h->unreadCount; + } + } HistoryBlock *block = showFrom->block(); - unreadBar = new HistoryUnreadBar(this, block, unreadCount, showFrom->date); + unreadBar = new HistoryUnreadBar(this, block, count, showFrom->date); if (!addNewInTheMiddle(unreadBar, blocks.indexOf(block), block->items.indexOf(showFrom))) { unreadBar = 0; } @@ -2359,6 +2381,10 @@ bool History::loadedAtTop() const { } bool History::isReadyFor(MsgId msgId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop) { + if (msgId < 0 && -msgId < ServerMaxMsgId && peer->migrateFrom()) { // old group history + return App::history(peer->migrateFrom()->id)->isReadyFor(-msgId, fixInScrollMsgId, fixInScrollMsgTop); + } + if (msgId != ShowAtTheEndMsgId && msgId != ShowAtUnreadMsgId && isChannel()) { return asChannelHistory()->isSwitchReadyFor(msgId, fixInScrollMsgId, fixInScrollMsgTop); } @@ -2368,6 +2394,13 @@ bool History::isReadyFor(MsgId msgId, MsgId &fixInScrollMsgId, int32 &fixInScrol return loadedAtBottom(); } if (msgId == ShowAtUnreadMsgId) { + if (peer->migrateFrom()) { // old group history + if (History *h = App::historyLoaded(peer->migrateFrom()->id)) { + if (h->unreadCount) { + return h->isReadyFor(msgId, fixInScrollMsgId, fixInScrollMsgTop); + } + } + } if (unreadCount) { if (!isEmpty()) { return (loadedAtTop() || minMsgId() <= inboxReadBefore) && (loadedAtBottom() || maxMsgId() >= inboxReadBefore); @@ -2381,9 +2414,30 @@ bool History::isReadyFor(MsgId msgId, MsgId &fixInScrollMsgId, int32 &fixInScrol } void History::getReadyFor(MsgId msgId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop) { + if (msgId < 0 && -msgId < ServerMaxMsgId && peer->migrateFrom()) { + History *h = App::history(peer->migrateFrom()->id); + h->getReadyFor(-msgId, fixInScrollMsgId, fixInScrollMsgTop); + if (h->isEmpty()) { + clear(true); + newLoaded = oldLoaded = false; + lastWidth = 0; + } + return; + } if (msgId != ShowAtTheEndMsgId && msgId != ShowAtUnreadMsgId && isChannel()) { return asChannelHistory()->getSwitchReadyFor(msgId, fixInScrollMsgId, fixInScrollMsgTop); } + if (msgId == ShowAtUnreadMsgId && peer->migrateFrom()) { + if (History *h = App::historyLoaded(peer->migrateFrom()->id)) { + if (h->unreadCount) { + clear(true); + newLoaded = oldLoaded = false; + lastWidth = 0; + h->getReadyFor(msgId, fixInScrollMsgId, fixInScrollMsgTop); + return; + } + } + } if (!isReadyFor(msgId, fixInScrollMsgId, fixInScrollMsgTop)) { clear(true); newLoaded = (msgId == ShowAtTheEndMsgId); @@ -2417,6 +2471,9 @@ void History::setLastMessage(HistoryItem *msg) { void History::setPosInDialogsDate(const QDateTime &date) { bool updateDialog = (App::main() && (!peer->isChannel() || peer->asChannel()->amIn() || !dialogs.isEmpty())); + if (peer->migrateTo() && dialogs.isEmpty()) { + updateDialog = false; + } if (!lastMsgDate.isNull() && lastMsgDate >= date) { if (!updateDialog || !dialogs.isEmpty()) { return; @@ -2552,6 +2609,12 @@ void History::blockResized(HistoryBlock *block, int32 dh) { } } +void History::clearUpto(MsgId msgId) { + for (HistoryItem *item = isEmpty() ? 0 : blocks.back()->items.back(); item && (item->id < 0 || item->id >= msgId); item = isEmpty() ? 0 : blocks.back()->items.back()) { + item->destroy(); + } +} + void History::removeBlock(HistoryBlock *block) { int32 i = blocks.indexOf(block), h = block->height; if (i >= 0) { @@ -2747,7 +2810,7 @@ bool ItemAnimations::animStep(float64 ms) { for (Animations::iterator i = _animations.begin(); i != _animations.end();) { const HistoryItem *item = i.key(); if (item->animating()) { - App::main()->msgUpdated(item->history()->peer->id, item); + App::main()->msgUpdated(item); ++i; } else { i = _animations.erase(i); @@ -6471,14 +6534,14 @@ void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, bool select } void HistoryMessage::setViewsCount(int32 count) { - if (_views == count || (_views >= 0 && count >= 0 && _views > count)) return; + if (_views == count || (count >= 0 && _views > count)) return; int32 was = _viewsWidth; _views = count; _viewsText = (_views >= 0) ? formatViewsCount(_views) : QString(); _viewsWidth = _viewsText.isEmpty() ? 0 : st::msgDateFont->width(_viewsText); if (was == _viewsWidth) { - if (App::main()) App::main()->msgUpdated(history()->peer->id, this); + if (App::main()) App::main()->msgUpdated(this); } else { if (_text.hasSkipBlock()) { _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); @@ -6494,7 +6557,7 @@ void HistoryMessage::setId(MsgId newId) { bool wasPositive = (id > 0), positive = (newId > 0); id = newId; if (wasPositive == positive) { - if (App::main()) App::main()->msgUpdated(history()->peer->id, this); + if (App::main()) App::main()->msgUpdated(this); } else { if (_text.hasSkipBlock()) { _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); @@ -6511,7 +6574,7 @@ void HistoryMessage::draw(Painter &p, uint32 selection) const { textstyleSet(&(outbg ? st::outTextStyle : st::inTextStyle)); - uint64 ms = App::main() ? App::main()->animActiveTime(id) : 0; + uint64 ms = App::main() ? App::main()->animActiveTime(this) : 0; if (ms) { if (ms > st::activeFadeInDuration + st::activeFadeOutDuration) { App::main()->stopAnimActive(); @@ -7403,23 +7466,42 @@ HistoryReply::~HistoryReply() { } void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) { - TextLinkPtr second; + TextLinkPtr second, third; LangString text = lang(lng_message_empty); QString from = textcmdLink(1, _from->name); switch (action.type()) { case mtpc_messageActionChatAddUser: { const MTPDmessageActionChatAddUser &d(action.c_messageActionChatAddUser()); - if (peerFromUser(d.vuser_id) == _from->id) { - text = lng_action_user_joined(lt_from, from); + const QVector &v(d.vusers.c_vector().v); + bool foundSelf = false; + for (int32 i = 0, l = v.size(); i < l; ++i) { + if (v.at(i).v == MTP::authedId()) { + foundSelf = true; + break; + } + } + if (v.size() == 1) { + UserData *u = App::user(peerFromUser(v.at(0))); + if (u == _from) { + text = lng_action_user_joined(lt_from, from); + } else { + second = TextLinkPtr(new PeerLink(u)); + text = lng_action_add_user(lt_from, from, lt_user, textcmdLink(2, u->name)); + } + } else if (v.size() == 2) { + UserData *u1 = App::user(peerFromUser(v.at(0))), *u2 = App::user(peerFromUser(v.at(1))); + second = TextLinkPtr(new PeerLink(u1)); + third = TextLinkPtr(new PeerLink(u2)); + text = lng_action_add_user_and_user(lt_from, from, lt_user, textcmdLink(2, u1->name), lt_second_user, textcmdLink(3, u2->name)); } else { - UserData *u = App::user(peerFromUser(d.vuser_id)); + UserData *u = App::user(peerFromUser(foundSelf ? MTP::authedId() : v.at(0).v)); second = TextLinkPtr(new PeerLink(u)); - text = lng_action_add_user(lt_from, from, lt_user, textcmdLink(2, u->name)); - if (d.vuser_id.v == MTP::authedId() && unread()) { - if (history()->peer->isChat() && !history()->peer->asChat()->inviterForSpamReport && _from->isUser()) { - history()->peer->asChat()->inviterForSpamReport = peerToUser(_from->id); - } + text = lng_action_add_users(lt_from, from, lt_user, textcmdLink(2, u->name), lt_count, v.size() - 1); + } + if (unread() && foundSelf) { + if (history()->peer->isChat() && !history()->peer->asChat()->inviterForSpamReport && _from->isUser()) { + history()->peer->asChat()->inviterForSpamReport = peerToUser(_from->id); } } } break; @@ -7482,15 +7564,8 @@ void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) { text = fromChannel() ? lng_action_changed_title_channel(lt_title, textClean(qs(d.vtitle))) : lng_action_changed_title(lt_from, from, lt_title, textClean(qs(d.vtitle))); } break; - case mtpc_messageActionChatDeactivate: { - text = lang(lng_action_group_deactivate); - } break; - - case mtpc_messageActionChatActivate: { - text = lang(lng_action_group_activate); - } break; - case mtpc_messageActionChatMigrateTo: { + _flags |= MTPDmessage_flag_IS_GROUP_MIGRATE; const MTPDmessageActionChatMigrateTo &d(action.c_messageActionChatMigrateTo()); if (true/*PeerData *channel = App::peerLoaded(peerFromChannel(d.vchannel_id))*/) { text = lang(lng_action_group_migrate); @@ -7500,13 +7575,10 @@ void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) { } break; case mtpc_messageActionChannelMigrateFrom: { + _flags |= MTPDmessage_flag_IS_GROUP_MIGRATE; 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); } @@ -7522,6 +7594,9 @@ void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) { if (second) { _text.setLink(2, second); } + if (third) { + _text.setLink(3, third); + } return ; } @@ -7567,7 +7642,7 @@ void HistoryServiceMsg::setServiceText(const QString &text) { } void HistoryServiceMsg::draw(Painter &p, uint32 selection) const { - uint64 ms = App::main() ? App::main()->animActiveTime(id) : 0; + uint64 ms = App::main() ? App::main()->animActiveTime(this) : 0; if (ms) { if (ms > st::activeFadeInDuration + st::activeFadeOutDuration) { App::main()->stopAnimActive(); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 849aaf894..fe37497ac 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -193,6 +193,7 @@ public: return blocks.isEmpty(); } void clear(bool leaveItems = false); + void clearUpto(MsgId msgId); void blockResized(HistoryBlock *block, int32 dh); void removeBlock(HistoryBlock *block); @@ -834,8 +835,14 @@ public: return _flags & MTPDmessage::flag_out; } bool unread() const { - if ((out() && (id > 0 && id < _history->outboxReadBefore)) || (!out() && id > 0 && id < _history->inboxReadBefore)) return false; - return (id > 0 && !out() && channelId() != NoChannel) ? true : (history()->peer->isSelf() ? false : (_flags & MTPDmessage::flag_unread)); + if (out() && id > 0 && id < _history->outboxReadBefore) return false; + if (!out() && id > 0) { + if (id < _history->inboxReadBefore) return false; + if (channelId() != NoChannel) return true; // no unread flag for incoming messages in channels + } + if (history()->peer->isSelf()) return false; // messages from myself are always read + if (out() && history()->peer->migrateTo()) return false; // outgoing messages in converted chats are always read + return (_flags & MTPDmessage::flag_unread); } bool mentionsMe() const { return _flags & MTPDmessage::flag_mentioned; @@ -852,6 +859,9 @@ public: bool hasTextLinks() const { return _flags & MTPDmessage_flag_HAS_TEXT_LINKS; } + bool isGroupMigrate() const { + return _flags & MTPDmessage_flag_IS_GROUP_MIGRATE; + } bool hasViews() const { return _flags & MTPDmessage::flag_views; } @@ -917,7 +927,7 @@ public: bool canDelete() const { ChannelData *channel = _history->peer->asChannel(); - if (!channel) return true; + if (!channel) return !(_flags & MTPDmessage_flag_IS_GROUP_MIGRATE); if (id == 1) return false; if (channel->amCreator()) return true; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index b05ada7e2..04ea91987 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -39,39 +39,44 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html HistoryInner::HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, History *history) : QWidget(0) - , hist(history) - , ySkip(0) - , botInfo(history->peer->isUser() ? history->peer->asUser()->botInfo : 0) - , botDescWidth(0), botDescHeight(0) - , historyWidget(historyWidget) - , scrollArea(scroll) - , currentBlock(0) - , currentItem(0) - , _firstLoading(false) - , _cursor(style::cur_default) - , _dragAction(NoDrag) - , _dragSelType(TextSelectLetters) - , _dragItem(0) - , _dragCursorState(HistoryDefaultCursorState) - , _dragWasInactive(false) - , _dragSelFrom(0) - , _dragSelTo(0) - , _dragSelecting(false) - , _wasSelectedText(false) - , _touchScroll(false) - , _touchSelect(false) - , _touchInProgress(false) - , _touchScrollState(TouchScrollManual) - , _touchPrevPosValid(false) - , _touchWaitingAcceleration(false) - , _touchSpeedTime(0) - , _touchAccelerationTime(0) - , _touchTime(0) - , _menu(0) { +, _peer(history->peer) +, _migrated(history->peer->migrateFrom() ? App::history(history->peer->migrateFrom()->id) : 0) +, _history(history) +, _historyOffset(0) +, _historySkipHeight(0) +, _botInfo(history->peer->isUser() ? history->peer->asUser()->botInfo : 0) +, _botDescWidth(0) +, _botDescHeight(0) +, _widget(historyWidget) +, _scroll(scroll) +, _curHistory(0) +, _curBlock(0) +, _curItem(0) +, _firstLoading(false) +, _cursor(style::cur_default) +, _dragAction(NoDrag) +, _dragSelType(TextSelectLetters) +, _dragItem(0) +, _dragCursorState(HistoryDefaultCursorState) +, _dragWasInactive(false) +, _dragSelFrom(0) +, _dragSelTo(0) +, _dragSelecting(false) +, _wasSelectedText(false) +, _touchScroll(false) +, _touchSelect(false) +, _touchInProgress(false) +, _touchScrollState(TouchScrollManual) +, _touchPrevPosValid(false) +, _touchWaitingAcceleration(false) +, _touchSpeedTime(0) +, _touchAccelerationTime(0) +, _touchTime(0) +, _menu(0) { connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); - linkTipTimer.setSingleShot(true); - connect(&linkTipTimer, SIGNAL(timeout()), this, SLOT(showLinkTip())); + _tooltipTimer.setSingleShot(true); + connect(&_tooltipTimer, SIGNAL(timeout()), this, SLOT(showLinkTip())); _touchSelectTimer.setSingleShot(true); connect(&_touchSelectTimer, SIGNAL(timeout()), this, SLOT(onTouchSelect())); @@ -80,24 +85,43 @@ HistoryInner::HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, His _trippleClickTimer.setSingleShot(true); - if (botInfo && !botInfo->inited && App::api()) { - App::api()->requestFullPeer(hist->peer); + if (_botInfo && !_botInfo->inited && App::api()) { + App::api()->requestFullPeer(_peer); } setMouseTracking(true); } -void HistoryInner::messagesReceived(const QVector &messages, const QVector *collapsed) { - hist->addOlderSlice(messages, collapsed); +void HistoryInner::messagesReceived(PeerData *peer, const QVector &messages, const QVector *collapsed) { + if (_history && _history->peer == peer) { + _history->addOlderSlice(messages, collapsed); + } else if (_migrated && _migrated->peer == peer) { + bool newLoaded = (_migrated && _migrated->isEmpty() && !_history->isEmpty()); + _migrated->addOlderSlice(messages, collapsed); + if (newLoaded) { + _migrated->addNewerSlice(QVector(), 0); + } + } } -void HistoryInner::messagesReceivedDown(const QVector &messages, const QVector *collapsed) { - hist->addNewerSlice(messages, collapsed); +void HistoryInner::messagesReceivedDown(PeerData *peer, const QVector &messages, const QVector *collapsed) { + if (_history && _history->peer == peer) { + bool oldLoaded = (_migrated && _history->isEmpty() && !_migrated->isEmpty()); + _history->addNewerSlice(messages, collapsed); + if (oldLoaded) { + _history->addOlderSlice(QVector(), 0); + } + } else if (_migrated && _migrated->peer == peer) { + _migrated->addNewerSlice(messages, collapsed); + } } void HistoryInner::updateMsg(const HistoryItem *msg) { - if (!msg || msg->detached() || !hist || hist != msg->history()) return; - update(0, ySkip + msg->block()->y + msg->y, width(), msg->height()); + if (!msg || msg->detached() || !_history) return; + int32 msgy = itemTop(msg); + if (msgy >= 0) { + update(0, msgy, width(), msg->height()); + } } void HistoryInner::paintEvent(QPaintEvent *e) { @@ -113,70 +137,123 @@ void HistoryInner::paintEvent(QPaintEvent *e) { p.setClipRect(r); } - if (!_firstLoading && botInfo && !botInfo->text.isEmpty() && botDescHeight > 0) { - if (r.y() < botDescRect.y() + botDescRect.height() && r.y() + r.height() > botDescRect.y()) { + if (!_firstLoading && _botInfo && !_botInfo->text.isEmpty() && _botDescHeight > 0) { + if (r.y() < _botDescRect.y() + _botDescRect.height() && r.y() + r.height() > _botDescRect.y()) { textstyleSet(&st::inTextStyle); - App::roundRect(p, botDescRect, st::msgInBg, MessageInCorners, &st::msgInShadow); + App::roundRect(p, _botDescRect, st::msgInBg, MessageInCorners, &st::msgInShadow); p.setFont(st::msgNameFont->f); p.setPen(st::black->p); - p.drawText(botDescRect.left() + st::msgPadding.left(), botDescRect.top() + st::msgPadding.top() + st::msgNameFont->ascent, lang(lng_bot_description)); + p.drawText(_botDescRect.left() + st::msgPadding.left(), _botDescRect.top() + st::msgPadding.top() + st::msgNameFont->ascent, lang(lng_bot_description)); - botInfo->text.draw(p, botDescRect.left() + st::msgPadding.left(), botDescRect.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip, botDescWidth); + _botInfo->text.draw(p, _botDescRect.left() + st::msgPadding.left(), _botDescRect.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip, _botDescWidth); textstyleRestore(); } - } else if (_firstLoading || hist->isEmpty()) { + } else if (_firstLoading || (_history->isEmpty() && (!_migrated || _migrated->isEmpty()))) { QPoint dogPos((width() - st::msgDogImg.pxWidth()) / 2, ((height() - st::msgDogImg.pxHeight()) * 4) / 9); p.drawPixmap(dogPos, *cChatDogImage()); } - if (!_firstLoading && !hist->isEmpty()) { + if (!_firstLoading) { adjustCurrent(r.top()); - HistoryBlock *block = hist->blocks[currentBlock]; - HistoryItem *item = block->items[currentItem]; SelectedItems::const_iterator selEnd = _selected.cend(); bool hasSel = !_selected.isEmpty(); - int32 drawToY = r.y() + r.height() - ySkip; + int32 drawToY = r.y() + r.height(); - int32 selfromy = 0, seltoy = 0; - if (_dragSelFrom && _dragSelTo) { - selfromy = _dragSelFrom->y + _dragSelFrom->block()->y; - seltoy = _dragSelTo->y + _dragSelTo->block()->y + _dragSelTo->height(); + int32 selfromy = itemTop(_dragSelFrom), seltoy = itemTop(_dragSelTo); + if (selfromy < 0 || seltoy < 0) { + selfromy = seltoy = -1; + } else { + seltoy += _dragSelTo->height(); } - int32 iBlock = currentBlock, iItem = currentItem, y = block->y + item->y; - p.translate(0, ySkip + y); - while (y < drawToY) { - int32 h = item->height(); - uint32 sel = 0; - if (y >= selfromy && y < seltoy) { - sel = (_dragSelecting && !item->serviceMsg() && item->id > 0) ? FullItemSel : 0; - } else if (hasSel) { - SelectedItems::const_iterator i = _selected.constFind(item); - if (i != selEnd) { - sel = i.value(); - } - } - item->draw(p, sel); + int32 mtop = migratedTop(), htop = historyTop(), hdrawtop = historyDrawTop(); + if (mtop >= 0) { + int32 iBlock = (_curHistory == _migrated ? _curBlock : (_migrated->blocks.size() - 1)); + HistoryBlock *block = _migrated->blocks[iBlock]; + int32 iItem = (_curHistory == _migrated ? _curItem : (block->items.size() - 1)); + HistoryItem *item = block->items[iItem]; - if (item->hasViews()) { - App::main()->scheduleViewIncrement(item); - } - - p.translate(0, h); - ++iItem; - if (iItem == block->items.size()) { - iItem = 0; - ++iBlock; - if (iBlock == hist->blocks.size()) { - break; + int32 y = mtop + block->y + item->y; + p.save(); + p.translate(0, y); + if (r.y() < y + item->height()) while (y < drawToY) { + uint32 sel = 0; + if (y >= selfromy && y < seltoy) { + sel = (_dragSelecting && !item->serviceMsg() && item->id > 0) ? FullItemSel : 0; + } else if (hasSel) { + SelectedItems::const_iterator i = _selected.constFind(item); + if (i != selEnd) { + sel = i.value(); + } } - block = hist->blocks[iBlock]; + item->draw(p, sel); + + if (item->hasViews()) { + App::main()->scheduleViewIncrement(item); + } + + int32 h = item->height(); + p.translate(0, h); + y += h; + + ++iItem; + if (iItem == block->items.size()) { + iItem = 0; + ++iBlock; + if (iBlock == _migrated->blocks.size()) { + break; + } + block = _migrated->blocks[iBlock]; + } + item = block->items[iItem]; } - item = block->items[iItem]; - y += h; + p.restore(); + } + if (htop >= 0) { + int32 iBlock = (_curHistory == _history ? _curBlock : 0); + HistoryBlock *block = _history->blocks[iBlock]; + int32 iItem = (_curHistory == _history ? _curItem : 0); + HistoryItem *item = block->items[iItem]; + + int32 y = htop + block->y + item->y; + p.save(); + p.translate(0, y); + while (y < drawToY) { + int32 h = item->height(); + if (r.y() < y + h && hdrawtop < y + h) { + uint32 sel = 0; + if (y >= selfromy && y < seltoy) { + sel = (_dragSelecting && !item->serviceMsg() && item->id > 0) ? FullItemSel : 0; + } else if (hasSel) { + SelectedItems::const_iterator i = _selected.constFind(item); + if (i != selEnd) { + sel = i.value(); + } + } + item->draw(p, sel); + + if (item->hasViews()) { + App::main()->scheduleViewIncrement(item); + } + } + p.translate(0, h); + y += h; + + ++iItem; + if (iItem == block->items.size()) { + iItem = 0; + ++iBlock; + if (iBlock == _history->blocks.size()) { + break; + } + block = _history->blocks[iBlock]; + } + item = block->items[iItem]; + } + p.restore(); } } } @@ -200,7 +277,7 @@ void HistoryInner::onTouchScrollTimer() { } else if (_touchScrollState == TouchScrollAuto || _touchScrollState == TouchScrollAcceleration) { int32 elapsed = int32(nowTime - _touchTime); QPoint delta = _touchSpeed * elapsed / 1000; - bool hasScrolled = historyWidget->touchScroll(delta); + bool hasScrolled = _widget->touchScroll(delta); if (_touchSpeed.isNull() || !hasScrolled) { _touchScrollState = TouchScrollManual; @@ -374,13 +451,14 @@ void HistoryInner::dragActionUpdate(const QPoint &screenPos) { void HistoryInner::touchScrollUpdated(const QPoint &screenPos) { _touchPos = screenPos; - historyWidget->touchScroll(_touchPos - _touchPrevPos); + _widget->touchScroll(_touchPos - _touchPrevPos); touchUpdateSpeed(); } QPoint HistoryInner::mapMouseToItem(QPoint p, HistoryItem *item) { - if (!item || item->detached()) return QPoint(0, 0); - p.setY(p.y() - (height() - hist->height - st::historyPadding) - item->block()->y - item->y); + int32 msgy = itemTop(item); + if (msgy < 0) return QPoint(0, 0); + p.setY(p.y() - msgy); return p; } @@ -505,7 +583,7 @@ void HistoryInner::dragActionCancel() { _dragStartPos = QPoint(0, 0); _dragSelFrom = _dragSelTo = 0; _wasSelectedText = false; - historyWidget->noSelectingScroll(); + _widget->noSelectingScroll(); } void HistoryInner::onDragExec() { @@ -546,7 +624,7 @@ void HistoryInner::onDragExec() { } if (!sel.isEmpty()) { updateDragSelection(0, 0, false); - historyWidget->noSelectingScroll(); + _widget->noSelectingScroll(); QDrag *drag = new QDrag(App::wnd()); QMimeData *mimeData = new QMimeData; @@ -600,7 +678,7 @@ void HistoryInner::itemRemoved(HistoryItem *item) { SelectedItems::iterator i = _selected.find(item); if (i != _selected.cend()) { _selected.erase(i); - historyWidget->updateTopBarSelection(); + _widget->updateTopBarSelection(); } if (_dragAction == NoDrag) return; @@ -715,8 +793,8 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but } _dragAction = NoDrag; _dragSelType = TextSelectLetters; - historyWidget->noSelectingScroll(); - historyWidget->updateTopBarSelection(); + _widget->noSelectingScroll(); + _widget->updateTopBarSelection(); } void HistoryInner::mouseReleaseEvent(QMouseEvent *e) { @@ -727,7 +805,7 @@ void HistoryInner::mouseReleaseEvent(QMouseEvent *e) { } void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) { - if (!hist) return; + if (!_history) return; if (((_dragAction == Selecting && !_selected.isEmpty() && _selected.cbegin().value() != FullItemSel) || (_dragAction == NoDrag && (_selected.isEmpty() || _selected.cbegin().value() != FullItemSel))) && _dragSelType == TextSelectLetters && _dragItem) { bool afterDragSymbol, uponSelected; @@ -766,7 +844,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { int32 selectedForForward, selectedForDelete; getSelectionState(selectedForForward, selectedForDelete); - bool canSendMessages = historyWidget->canSendMessages(hist->peer); + bool canSendMessages = _widget->canSendMessages(_peer); // -2 - has full selected items, but not over, -1 - has selection, but no over, 0 - no selection, 1 - over text, 2 - over full selected items int32 isUponSelected = 0, hasSelected = 0;; @@ -808,7 +886,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); } if (item && item->id > 0 && isUponSelected != 2 && isUponSelected != -2 && canSendMessages) { - _menu->addAction(lang(lng_context_reply_msg), historyWidget, SLOT(onReplyToMessage())); + _menu->addAction(lang(lng_context_reply_msg), _widget, SLOT(onReplyToMessage())); } if (lnkPhoto) { _menu->addAction(lang(lng_context_open_image), this, SLOT(openContextUrl()))->setEnabled(true); @@ -826,22 +904,22 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } if (isUponSelected > 1) { - _menu->addAction(lang(lng_context_forward_selected), historyWidget, SLOT(onForwardSelected())); + _menu->addAction(lang(lng_context_forward_selected), _widget, SLOT(onForwardSelected())); if (selectedForDelete == selectedForForward) { - _menu->addAction(lang(lng_context_delete_selected), historyWidget, SLOT(onDeleteSelected())); + _menu->addAction(lang(lng_context_delete_selected), _widget, SLOT(onDeleteSelected())); } - _menu->addAction(lang(lng_context_clear_selection), historyWidget, SLOT(onClearSelected())); + _menu->addAction(lang(lng_context_clear_selection), _widget, SLOT(onClearSelected())); } else if (App::hoveredLinkItem()) { if (isUponSelected != -2) { if (dynamic_cast(App::hoveredLinkItem()) && App::hoveredLinkItem()->id > 0) { - _menu->addAction(lang(lng_context_forward_msg), historyWidget, SLOT(forwardMessage()))->setEnabled(true); + _menu->addAction(lang(lng_context_forward_msg), _widget, SLOT(forwardMessage()))->setEnabled(true); } if (App::hoveredLinkItem()->canDelete()) { - _menu->addAction(lang(lng_context_delete_msg), historyWidget, SLOT(deleteMessage()))->setEnabled(true); + _menu->addAction(lang(lng_context_delete_msg), _widget, SLOT(deleteMessage()))->setEnabled(true); } } if (App::hoveredLinkItem()->id > 0 && !App::hoveredLinkItem()->serviceMsg()) { - _menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true); + _menu->addAction(lang(lng_context_select_msg), _widget, SLOT(selectMessage()))->setEnabled(true); } App::contextItem(App::hoveredLinkItem()); } @@ -856,19 +934,19 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (!_menu) _menu = new PopupMenu(); _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); if (item && item->id > 0 && isUponSelected != 2 && canSendMessages) { - _menu->addAction(lang(lng_context_reply_msg), historyWidget, SLOT(onReplyToMessage())); + _menu->addAction(lang(lng_context_reply_msg), _widget, SLOT(onReplyToMessage())); } } else { if (item && item->id > 0 && isUponSelected != -2 && canSendMessages) { if (!_menu) _menu = new PopupMenu(); - _menu->addAction(lang(lng_context_reply_msg), historyWidget, SLOT(onReplyToMessage())); + _menu->addAction(lang(lng_context_reply_msg), _widget, SLOT(onReplyToMessage())); } if (item && !isUponSelected && !_contextMenuLnk) { if (HistorySticker *sticker = dynamic_cast(msg ? msg->getMedia() : 0)) { DocumentData *doc = sticker->document(); if (doc && doc->sticker() && doc->sticker()->set.type() != mtpc_inputStickerSetEmpty) { if (!_menu) _menu = new PopupMenu(); - _menu->addAction(lang(doc->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), historyWidget, SLOT(onStickerPackInfo())); + _menu->addAction(lang(doc->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), _widget, SLOT(onStickerPackInfo())); } } QString contextMenuText = item->selectedText(FullItemSel); @@ -899,29 +977,29 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } if (isUponSelected > 1) { if (!_menu) _menu = new PopupMenu(); - _menu->addAction(lang(lng_context_forward_selected), historyWidget, SLOT(onForwardSelected())); + _menu->addAction(lang(lng_context_forward_selected), _widget, SLOT(onForwardSelected())); if (selectedForDelete == selectedForForward) { - _menu->addAction(lang(lng_context_delete_selected), historyWidget, SLOT(onDeleteSelected())); + _menu->addAction(lang(lng_context_delete_selected), _widget, SLOT(onDeleteSelected())); } - _menu->addAction(lang(lng_context_clear_selection), historyWidget, SLOT(onClearSelected())); + _menu->addAction(lang(lng_context_clear_selection), _widget, SLOT(onClearSelected())); } else if (item && ((isUponSelected != -2 && (canForward || canDelete)) || item->id > 0)) { if (!_menu) _menu = new PopupMenu(); if (isUponSelected != -2) { if (canForward) { - _menu->addAction(lang(lng_context_forward_msg), historyWidget, SLOT(forwardMessage()))->setEnabled(true); + _menu->addAction(lang(lng_context_forward_msg), _widget, SLOT(forwardMessage()))->setEnabled(true); } if (canDelete) { - _menu->addAction(lang((msg && msg->uploading()) ? lng_context_cancel_upload : lng_context_delete_msg), historyWidget, SLOT(deleteMessage()))->setEnabled(true); + _menu->addAction(lang((msg && msg->uploading()) ? lng_context_cancel_upload : lng_context_delete_msg), _widget, SLOT(deleteMessage()))->setEnabled(true); } } if (item->id > 0 && !item->serviceMsg()) { - _menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true); + _menu->addAction(lang(lng_context_select_msg), _widget, SLOT(selectMessage()))->setEnabled(true); } } else { if (App::mousedItem() && !App::mousedItem()->serviceMsg() && App::mousedItem()->id > 0) { if (!_menu) _menu = new PopupMenu(); - _menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true); + _menu->addAction(lang(lng_context_select_msg), _widget, SLOT(selectMessage()))->setEnabled(true); item = App::mousedItem(); } } @@ -1023,12 +1101,10 @@ void HistoryInner::saveContextFile() { void HistoryInner::copyContextText() { HistoryItem *item = App::contextItem(); - if (item && item->type() != HistoryItemMsg) { - item = 0; + if (!item || (item->getMedia() && item->getMedia()->type() == MediaTypeSticker)) { + return; } - if (!item) return; - QString contextMenuText = item->selectedText(FullItemSel); if (!contextMenuText.isEmpty()) { QApplication::clipboard()->setText(contextMenuText); @@ -1051,7 +1127,7 @@ QString HistoryInner::getSelectedText() const { return sel.cbegin().key()->selectedText(sel.cbegin().value()); } - int32 fullSize = 0; + int32 fullSize = 0, mtop = migratedTop(), htop = historyTop(); QString timeFormat(qsl(", [dd.MM.yy hh:mm]\n")); QMap texts; for (SelectedItems::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) { @@ -1061,8 +1137,12 @@ QString HistoryInner::getSelectedText() const { QString text, sel = item->selectedText(FullItemSel), time = item->date.toString(timeFormat); int32 size = item->from()->name.size() + time.size() + sel.size(); text.reserve(size); - texts.insert(item->y + item->block()->y, text.append(item->from()->name).append(time).append(sel)); - fullSize += size; + + int32 y = itemTop(item); + if (y >= 0) { + texts.insert(y, text.append(item->from()->name).append(time).append(sel)); + fullSize += size; + } } QString result, sep(qsl("\n\n")); @@ -1078,14 +1158,14 @@ QString HistoryInner::getSelectedText() const { void HistoryInner::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { - historyWidget->onListEscapePressed(); + _widget->onListEscapePressed(); } else if (e == QKeySequence::Copy && !_selected.isEmpty()) { copySelectedText(); } else if (e == QKeySequence::Delete) { int32 selectedForForward, selectedForDelete; getSelectionState(selectedForForward, selectedForDelete); if (!_selected.isEmpty() && selectedForDelete == selectedForForward) { - historyWidget->onDeleteSelected(); + _widget->onDeleteSelected(); } } else { e->ignore(); @@ -1093,79 +1173,109 @@ void HistoryInner::keyPressEvent(QKeyEvent *e) { } int32 HistoryInner::recountHeight(HistoryItem *resizedItem) { - int32 st = hist->lastScrollTop; + int32 htop = historyTop(), mtop = migratedTop(); + int32 st1 = (htop >= 0) ? (_history->lastScrollTop - htop) : -1, st2 = (_migrated && mtop >= 0) ? (_history->lastScrollTop - mtop) : -1; - int32 ph = scrollArea->height(), minadd = 0; - int32 wasYSkip = ph - (hist->height + st::historyPadding); - if (botInfo && !botInfo->text.isEmpty()) { - minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + botDescHeight; + int32 ph = _scroll->height(), minadd = 0; + int32 wasYSkip = ph - historyHeight() - st::historyPadding; + if (_botInfo && !_botInfo->text.isEmpty()) { + minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botDescHeight; } if (wasYSkip < minadd) wasYSkip = minadd; - hist->geomResize(scrollArea->width(), &st, resizedItem); + if (resizedItem) { + if (resizedItem->history() == _history) { + _history->geomResize(_scroll->width(), &st1, resizedItem); + } else if (_migrated && resizedItem->history() == _migrated) { + _migrated->geomResize(_scroll->width(), &st2, resizedItem); + } + } else { + _history->geomResize(_scroll->width(), &st1, resizedItem); + if (_migrated) { + _migrated->geomResize(_scroll->width(), &st2, resizedItem); + } + } + int32 skip = 0; + if (_migrated) { // check first messages of _history - maybe no need to display them + if (!_migrated->isEmpty() && !_history->isEmpty() && _migrated->loadedAtBottom() && _history->loadedAtTop()) { + if (_migrated->blocks.back()->items.back()->date.date() == _history->blocks.front()->items.front()->date.date()) { + skip += _history->blocks.front()->items.front()->height(); + if (_migrated->blocks.back()->items.back()->isGroupMigrate() && _history->blocks.front()->items.size() == 1 && _history->blocks.size() > 1 && _history->blocks.at(1)->items.front()->isGroupMigrate()) { + skip += _history->blocks.at(1)->items.at(0)->height(); + } + } + if (skip > migratedTop() + _migrated->height) { + skip = migratedTop() + _migrated->height; // should not happen, just check.. we need historyTop() >= 0 + } + } + } + if (skip != _historySkipHeight) { + if (st1 >= 0) st1 -= (_historySkipHeight - skip); + _historySkipHeight = skip; + } updateBotInfo(false); - if (botInfo && !botInfo->text.isEmpty()) { - int32 tw = scrollArea->width() - st::msgMargin.left() - st::msgMargin.right(); + if (_botInfo && !_botInfo->text.isEmpty()) { + int32 tw = _scroll->width() - st::msgMargin.left() - st::msgMargin.right(); if (tw > st::msgMaxWidth) tw = st::msgMaxWidth; tw -= st::msgPadding.left() + st::msgPadding.right(); - int32 mw = qMax(botInfo->text.maxWidth(), st::msgNameFont->width(lang(lng_bot_description))); + int32 mw = qMax(_botInfo->text.maxWidth(), st::msgNameFont->width(lang(lng_bot_description))); if (tw > mw) tw = mw; - botDescWidth = tw; - botDescHeight = botInfo->text.countHeight(botDescWidth); + _botDescWidth = tw; + _botDescHeight = _botInfo->text.countHeight(_botDescWidth); - int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + botDescHeight + st::msgPadding.bottom() + st::msgMargin.bottom(); - int32 descAtX = (scrollArea->width() - botDescWidth) / 2 - st::msgPadding.left(); - int32 descAtY = qMin(ySkip - descH, qMax(0, (scrollArea->height() - descH) / 2)) + st::msgMargin.top(); + int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + _botDescHeight + st::msgPadding.bottom() + st::msgMargin.bottom(); + int32 descAtX = (_scroll->width() - _botDescWidth) / 2 - st::msgPadding.left(); + int32 descAtY = qMin(_historyOffset - descH, qMax(0, (_scroll->height() - descH) / 2)) + st::msgMargin.top(); - botDescRect = QRect(descAtX, descAtY, botDescWidth + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom()); + _botDescRect = QRect(descAtX, descAtY, _botDescWidth + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom()); } else { - botDescWidth = botDescHeight = 0; - botDescRect = QRect(); + _botDescWidth = _botDescHeight = 0; + _botDescRect = QRect(); } - int32 newYSkip = ph - (hist->height + st::historyPadding); - if (botInfo && !botInfo->text.isEmpty()) { - minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + botDescHeight; + int32 newYSkip = ph - historyHeight() - st::historyPadding; + if (_botInfo && !_botInfo->text.isEmpty()) { + minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botDescHeight; } if (newYSkip < minadd) newYSkip = minadd; - return st + (newYSkip - wasYSkip); + return ((st1 >= 0 || st2 < 0) ? (st1 + htop) : (st2 + mtop)) + (newYSkip - wasYSkip); } void HistoryInner::updateBotInfo(bool recount) { int32 newh = 0; - if (botInfo && !botInfo->description.isEmpty()) { - if (botInfo->text.isEmpty()) { - botInfo->text.setText(st::msgFont, botInfo->description, _historyBotNoMonoOptions); + if (_botInfo && !_botInfo->description.isEmpty()) { + if (_botInfo->text.isEmpty()) { + _botInfo->text.setText(st::msgFont, _botInfo->description, _historyBotNoMonoOptions); if (recount) { - int32 tw = scrollArea->width() - st::msgMargin.left() - st::msgMargin.right(); + int32 tw = _scroll->width() - st::msgMargin.left() - st::msgMargin.right(); if (tw > st::msgMaxWidth) tw = st::msgMaxWidth; tw -= st::msgPadding.left() + st::msgPadding.right(); - int32 mw = qMax(botInfo->text.maxWidth(), st::msgNameFont->width(lang(lng_bot_description))); + int32 mw = qMax(_botInfo->text.maxWidth(), st::msgNameFont->width(lang(lng_bot_description))); if (tw > mw) tw = mw; - botDescWidth = tw; - newh = botInfo->text.countHeight(botDescWidth); + _botDescWidth = tw; + newh = _botInfo->text.countHeight(_botDescWidth); } } else if (recount) { - newh = botDescHeight; + newh = _botDescHeight; } } if (recount) { - if (botDescHeight != newh) { - botDescHeight = newh; + if (_botDescHeight != newh) { + _botDescHeight = newh; updateSize(); } - if (botDescHeight > 0) { - int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + botDescHeight + st::msgPadding.bottom() + st::msgMargin.bottom(); - int32 descAtX = (scrollArea->width() - botDescWidth) / 2 - st::msgPadding.left(); - int32 descAtY = qMin(ySkip - descH, (scrollArea->height() - descH) / 2) + st::msgMargin.top(); + if (_botDescHeight > 0) { + int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + _botDescHeight + st::msgPadding.bottom() + st::msgMargin.bottom(); + int32 descAtX = (_scroll->width() - _botDescWidth) / 2 - st::msgPadding.left(); + int32 descAtY = qMin(_historyOffset - descH, (_scroll->height() - descH) / 2) + st::msgMargin.top(); - botDescRect = QRect(descAtX, descAtY, botDescWidth + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom()); + _botDescRect = QRect(descAtX, descAtY, _botDescWidth + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom()); } else { - botDescWidth = 0; - botDescRect = QRect(); + _botDescWidth = 0; + _botDescRect = QRect(); } } } @@ -1180,29 +1290,29 @@ void HistoryInner::setFirstLoading(bool loading) { } HistoryItem *HistoryInner::atTopImportantMsg(int32 top, int32 height, int32 &bottomUnderScrollTop) const { - if (hist->isEmpty()) return 0; - adjustCurrent(top); - for (int32 blockIndex = currentBlock + 1, itemIndex = currentItem + 1; blockIndex > 0;) { + if (!_curHistory || _curHistory->isEmpty() || _curHistory != _history) return 0; + + for (int32 blockIndex = _curBlock + 1, itemIndex = _curItem + 1; blockIndex > 0;) { --blockIndex; - HistoryBlock *block = hist->blocks[blockIndex]; + HistoryBlock *block = _history->blocks[blockIndex]; if (!itemIndex) itemIndex = block->items.size(); for (; itemIndex > 0;) { --itemIndex; HistoryItem *item = block->items[itemIndex]; if (item->isImportant()) { - bottomUnderScrollTop = qMin(0, ySkip + item->y + item->block()->y + item->height() - top); + bottomUnderScrollTop = qMin(0, itemTop(item) + item->height() - top); return item; } } itemIndex = 0; } - for (int32 blockIndex = currentBlock, itemIndex = currentItem + 1; blockIndex < hist->blocks.size(); ++blockIndex) { - HistoryBlock *block = hist->blocks[blockIndex]; + for (int32 blockIndex = _curBlock, itemIndex = _curItem + 1; blockIndex < _history->blocks.size(); ++blockIndex) { + HistoryBlock *block = _history->blocks[blockIndex]; for (; itemIndex < block->items.size(); ++itemIndex) { HistoryItem *item = block->items[itemIndex]; if (item->isImportant()) { - bottomUnderScrollTop = qMin(0, ySkip + item->y + item->block()->y + item->height() - top); + bottomUnderScrollTop = qMin(0, itemTop(item) + item->height() - top); return item; } } @@ -1212,27 +1322,27 @@ HistoryItem *HistoryInner::atTopImportantMsg(int32 top, int32 height, int32 &bot } void HistoryInner::updateSize() { - int32 ph = scrollArea->height(), minadd = 0; - int32 newYSkip = ph - (hist->height + st::historyPadding); - if (botInfo && !botInfo->text.isEmpty()) { - minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + botDescHeight; + int32 ph = _scroll->height(), minadd = 0; + int32 newYSkip = ph - historyHeight() - st::historyPadding; + if (_botInfo && !_botInfo->text.isEmpty()) { + minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botDescHeight; } if (newYSkip < minadd) newYSkip = minadd; - if (botDescHeight > 0) { - int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + botDescHeight + st::msgPadding.bottom() + st::msgMargin.bottom(); - int32 descAtX = (scrollArea->width() - botDescWidth) / 2 - st::msgPadding.left(); - int32 descAtY = qMin(newYSkip - descH, qMax(0, (scrollArea->height() - descH) / 2)) + st::msgMargin.top(); + if (_botDescHeight > 0) { + int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + _botDescHeight + st::msgPadding.bottom() + st::msgMargin.bottom(); + int32 descAtX = (_scroll->width() - _botDescWidth) / 2 - st::msgPadding.left(); + int32 descAtY = qMin(newYSkip - descH, qMax(0, (_scroll->height() - descH) / 2)) + st::msgMargin.top(); - botDescRect = QRect(descAtX, descAtY, botDescWidth + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom()); + _botDescRect = QRect(descAtX, descAtY, _botDescWidth + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom()); } - int32 yAdded = newYSkip - ySkip; - ySkip = newYSkip; + int32 yAdded = newYSkip - _historyOffset; + _historyOffset = newYSkip; - int32 nh = hist->height + st::historyPadding + ySkip; - if (width() != scrollArea->width() || height() != nh) { - resize(scrollArea->width(), nh); + int32 nh = _historyOffset + historyHeight() + st::historyPadding; + if (width() != _scroll->width() || height() != nh) { + resize(_scroll->width(), nh); dragActionUpdate(QCursor::pos()); } else { @@ -1265,43 +1375,56 @@ HistoryInner::~HistoryInner() { } void HistoryInner::adjustCurrent(int32 y) const { - if (hist->isEmpty()) return; - if (currentBlock >= hist->blocks.size()) { - currentBlock = hist->blocks.size() - 1; - currentItem = 0; + int32 htop = historyTop(), hdrawtop = historyDrawTop(), mtop = migratedTop(); + _curHistory = 0; + if (mtop >= 0) { + adjustCurrent(y - mtop, _migrated); } + if (htop >= 0 && hdrawtop >= 0 && (mtop < 0 || y >= hdrawtop)) { + adjustCurrent(y - htop, _history); + } +} - while (hist->blocks[currentBlock]->y + ySkip > y && currentBlock > 0) { - --currentBlock; - currentItem = 0; +void HistoryInner::adjustCurrent(int32 y, History *history) const { + _curHistory = history; + if (_curBlock >= history->blocks.size()) { + _curBlock = history->blocks.size() - 1; + _curItem = 0; } - while (hist->blocks[currentBlock]->y + hist->blocks[currentBlock]->height + ySkip <= y && currentBlock + 1 < hist->blocks.size()) { - ++currentBlock; - currentItem = 0; + while (history->blocks[_curBlock]->y > y && _curBlock > 0) { + --_curBlock; + _curItem = 0; } - HistoryBlock *block = hist->blocks[currentBlock]; - if (currentItem >= block->items.size()) { - currentItem = block->items.size() - 1; + while (history->blocks[_curBlock]->y + history->blocks[_curBlock]->height <= y && _curBlock + 1 < history->blocks.size()) { + ++_curBlock; + _curItem = 0; + } + HistoryBlock *block = history->blocks[_curBlock]; + if (_curItem >= block->items.size()) { + _curItem = block->items.size() - 1; } int32 by = block->y; - while (block->items[currentItem]->y + by + ySkip > y && currentItem > 0) { - --currentItem; + while (block->items[_curItem]->y + by > y && _curItem > 0) { + --_curItem; } - while (block->items[currentItem]->y + block->items[currentItem]->height() + by + ySkip <= y && currentItem + 1 < block->items.size()) { - ++currentItem; + while (block->items[_curItem]->y + block->items[_curItem]->height() + by <= y && _curItem + 1 < block->items.size()) { + ++_curItem; } } HistoryItem *HistoryInner::prevItem(HistoryItem *item) { if (!item) return 0; HistoryBlock *block = item->block(); - int32 blockIndex = hist->blocks.indexOf(block), itemIndex = block->items.indexOf(item); + int32 blockIndex = item->history()->blocks.indexOf(block), itemIndex = block->items.indexOf(item); if (blockIndex < 0 || itemIndex < 0) return 0; if (itemIndex > 0) { return block->items[itemIndex - 1]; } if (blockIndex > 0) { - return hist->blocks[blockIndex - 1]->items.back(); + return item->history()->blocks[blockIndex - 1]->items.back(); + } + if (item->history() == _history && _migrated && _history->loadedAtTop() && _migrated->loadedAtBottom() && !_migrated->isEmpty()) { + return _migrated->blocks.back()->items.back(); } return 0; } @@ -1309,13 +1432,16 @@ HistoryItem *HistoryInner::prevItem(HistoryItem *item) { HistoryItem *HistoryInner::nextItem(HistoryItem *item) { if (!item) return 0; HistoryBlock *block = item->block(); - int32 blockIndex = hist->blocks.indexOf(block), itemIndex = block->items.indexOf(item); + int32 blockIndex = item->history()->blocks.indexOf(block), itemIndex = block->items.indexOf(item); if (blockIndex < 0 || itemIndex < 0) return 0; if (itemIndex + 1 < block->items.size()) { return block->items[itemIndex + 1]; } - if (blockIndex + 1 < hist->blocks.size()) { - return hist->blocks[blockIndex + 1]->items.front(); + if (blockIndex + 1 < item->history()->blocks.size()) { + return item->history()->blocks[blockIndex + 1]->items.front(); + } + if (item->history() == _migrated && _history && _migrated->loadedAtBottom() && _history->loadedAtTop() && !_history->isEmpty()) { + return _history->blocks.front()->items.front(); } return 0; } @@ -1349,8 +1475,8 @@ void HistoryInner::getSelectionState(int32 &selectedForForward, int32 &selectedF void HistoryInner::clearSelectedItems(bool onlyTextSelection) { if (!_selected.isEmpty() && (!onlyTextSelection || _selected.cbegin().value() != FullItemSel)) { _selected.clear(); - historyWidget->updateTopBarSelection(); - historyWidget->update(); + _widget->updateTopBarSelection(); + _widget->update(); } } @@ -1359,8 +1485,12 @@ void HistoryInner::fillSelectedItems(SelectedItemSet &sel, bool forDelete) { for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { HistoryItem *item = i.key(); - if (dynamic_cast(item) && item->id > 0) { - sel.insert(item->id, item); + if (item && item->toHistoryMessage() && item->id > 0) { + if (item->history() == _migrated) { + sel.insert(item->id - ServerMaxMsgId, item); + } else { + sel.insert(item->id, item); + } } } } @@ -1372,8 +1502,8 @@ void HistoryInner::selectItem(HistoryItem *item) { return; } _selected.insert(item, FullItemSel); - historyWidget->updateTopBarSelection(); - historyWidget->update(); + _widget->updateTopBarSelection(); + _widget->update(); } void HistoryInner::onTouchSelect() { @@ -1382,19 +1512,19 @@ void HistoryInner::onTouchSelect() { } void HistoryInner::onUpdateSelected() { - if (!hist) return; + if (!_history) return; QPoint mousePos(mapFromGlobal(_dragPos)); - QPoint point(historyWidget->clampMousePosition(mousePos)); + QPoint point(_widget->clampMousePosition(mousePos)); HistoryBlock *block = 0; HistoryItem *item = 0; QPoint m; - if (!hist->isEmpty()) { - adjustCurrent(point.y()); - block = hist->blocks[currentBlock]; - item = block->items[currentItem]; + adjustCurrent(point.y()); + if (_curHistory && !_curHistory->isEmpty()) { + block = _curHistory->blocks[_curBlock]; + item = block->items[_curItem]; App::mousedItem(item); m = mapMouseToItem(point, item); @@ -1416,10 +1546,10 @@ void HistoryInner::onUpdateSelected() { bool lnkChanged = false, lnkInDesc = false; TextLinkPtr lnk; - if (point.y() < ySkip) { - if (botInfo && !botInfo->text.isEmpty() && botDescHeight > 0) { + if (point.y() < _historyOffset) { + if (_botInfo && !_botInfo->text.isEmpty() && _botDescHeight > 0) { bool inText = false; - botInfo->text.getState(lnk, inText, point.x() - botDescRect.left() - st::msgPadding.left(), point.y() - botDescRect.top() - st::msgPadding.top() - st::botDescSkip - st::msgNameFont->height, botDescWidth); + _botInfo->text.getState(lnk, inText, point.x() - _botDescRect.left() - st::msgPadding.left(), point.y() - _botDescRect.top() - st::msgPadding.top() - st::botDescSkip - st::msgNameFont->height, _botDescWidth); cursorState = inText ? HistoryInTextCursorState : HistoryDefaultCursorState; lnkInDesc = true; } @@ -1432,7 +1562,7 @@ void HistoryInner::onUpdateSelected() { if (App::hoveredLinkItem()) { updateMsg(App::hoveredLinkItem()); } else { - update(botDescRect); + update(_botDescRect); } } textlnkOver(lnk); @@ -1442,12 +1572,12 @@ void HistoryInner::onUpdateSelected() { if (App::hoveredLinkItem()) { updateMsg(App::hoveredLinkItem()); } else { - update(botDescRect); + update(_botDescRect); } } } if (lnk || cursorState == HistoryInDateCursorState) { - linkTipTimer.start(1000); + _tooltipTimer.start(1000); } if (_dragCursorState == HistoryInDateCursorState && cursorState != HistoryInDateCursorState) { QToolTip::hideText(); @@ -1473,7 +1603,7 @@ void HistoryInner::onUpdateSelected() { } cur = textlnkDown() ? style::cur_pointer : style::cur_default; if (_dragAction == Selecting) { - bool canSelectMany = (hist != 0); + bool canSelectMany = (_history != 0); if (item == _dragItem && item == App::hoveredItem() && !_selected.isEmpty() && _selected.cbegin().value() != FullItemSel) { bool afterSymbol, uponSymbol; uint16 second; @@ -1487,7 +1617,7 @@ void HistoryInner::onUpdateSelected() { } updateDragSelection(0, 0, false); } else if (canSelectMany) { - bool selectingDown = (_dragItem->block()->y < item->block()->y) || ((_dragItem->block() == item->block()) && (_dragItem->y < item->y || (_dragItem == item && _dragStartPos.y() < m.y()))); + bool selectingDown = (itemTop(_dragItem) < itemTop(item)) || (_dragItem == item && _dragStartPos.y() < m.y()); HistoryItem *dragSelFrom = _dragItem, *dragSelTo = item; if (!dragSelFrom->hasPoint(_dragStartPos.x(), _dragStartPos.y())) { // maybe exclude dragSelFrom if (selectingDown) { @@ -1534,10 +1664,10 @@ void HistoryInner::onUpdateSelected() { } } if (_dragAction == Selecting) { - historyWidget->checkSelectingScroll(mousePos); + _widget->checkSelectingScroll(mousePos); } else { updateDragSelection(0, 0, false); - historyWidget->noSelectingScroll(); + _widget->noSelectingScroll(); } if (lnkChanged || cur != _cursor) { @@ -1549,7 +1679,8 @@ void HistoryInner::updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dr if (_dragSelFrom != dragSelFrom || _dragSelTo != dragSelTo || _dragSelecting != dragSelecting) { _dragSelFrom = dragSelFrom; _dragSelTo = dragSelTo; - if (_dragSelFrom && _dragSelTo && _dragSelFrom->y + _dragSelFrom->block()->y > _dragSelTo->y + _dragSelTo->block()->y) { + int32 fromy = itemTop(_dragSelFrom), toy = itemTop(_dragSelTo); + if (fromy >= 0 && toy >= 0 && fromy > toy) { qSwap(_dragSelFrom, _dragSelTo); } _dragSelecting = dragSelecting; @@ -1564,46 +1695,106 @@ void HistoryInner::updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dr update(); } +int32 HistoryInner::historyHeight() const { + int32 result = 0; + if (!_history || _history->isEmpty()) { + result += _migrated ? _migrated->height : 0; + } else { + result += _history->height - _historySkipHeight + (_migrated ? _migrated->height : 0); + } + return result; +} + +int32 HistoryInner::migratedTop() const { + return (_migrated && !_migrated->isEmpty()) ? _historyOffset : -1; +} + +int32 HistoryInner::historyTop() const { + int32 mig = migratedTop(); + return (_history && !_history->isEmpty()) ? (mig >= 0 ? (mig + _migrated->height - _historySkipHeight) : _historyOffset) : -1; +} + +int32 HistoryInner::historyDrawTop() const { + int32 his = historyTop(); + return (his >= 0) ? (his + _historySkipHeight) : -1; +} + +int32 HistoryInner::itemTop(const HistoryItem *item) const { // -1 if should not be visible, -2 if bad history() + if (!item) return -2; + if (item->detached()) return -1; + + int32 top = (item->history() == _history) ? historyTop() : (item->history() == _migrated ? migratedTop() : -2); + return (top < 0) ? top : (top + item->y + item->block()->y); +} + void HistoryInner::applyDragSelection() { applyDragSelection(&_selected); } +void HistoryInner::addSelectionRange(SelectedItems *toItems, int32 fromblock, int32 fromitem, int32 toblock, int32 toitem, History *h) const { + if (fromblock >= 0 && fromitem >= 0 && toblock >= 0 && toitem >= 0) { + for (; fromblock <= toblock; ++fromblock) { + HistoryBlock *block = h->blocks[fromblock]; + for (int32 cnt = (fromblock < toblock) ? block->items.size() : (toitem + 1); fromitem < cnt; ++fromitem) { + HistoryItem *item = block->items[fromitem]; + SelectedItems::iterator i = toItems->find(item); + if (item->id > 0 && !item->serviceMsg()) { + if (i == toItems->cend()) { + if (toItems->size() >= MaxSelectedItems) break; + toItems->insert(item, FullItemSel); + } else if (i.value() != FullItemSel) { + *i = FullItemSel; + } + } else { + if (i != toItems->cend()) { + toItems->erase(i); + } + } + } + if (toItems->size() >= MaxSelectedItems) break; + fromitem = 0; + } + } +} + void HistoryInner::applyDragSelection(SelectedItems *toItems) const { + int32 selfromy = itemTop(_dragSelFrom), seltoy = itemTop(_dragSelTo); + if (selfromy < 0 || seltoy < 0) { + return; + } + seltoy += _dragSelTo->height(); + if (!toItems->isEmpty() && toItems->cbegin().value() != FullItemSel) { toItems->clear(); } - - int32 fromy = _dragSelFrom->y + _dragSelFrom->block()->y, toy = _dragSelTo->y + _dragSelTo->block()->y + _dragSelTo->height(); if (_dragSelecting) { - int32 fromblock = hist->blocks.indexOf(_dragSelFrom->block()), fromitem = _dragSelFrom->block()->items.indexOf(_dragSelFrom); - int32 toblock = hist->blocks.indexOf(_dragSelTo->block()), toitem = _dragSelTo->block()->items.indexOf(_dragSelTo); - if (fromblock >= 0 && fromitem >= 0 && toblock >= 0 && toitem >= 0) { - for (; fromblock <= toblock; ++fromblock) { - HistoryBlock *block = hist->blocks[fromblock]; - for (int32 cnt = (fromblock < toblock) ? block->items.size() : (toitem + 1); fromitem < cnt; ++fromitem) { - HistoryItem *item = block->items[fromitem]; - SelectedItems::iterator i = toItems->find(item); - if (item->id > 0 && !item->serviceMsg()) { - if (i == toItems->cend()) { - if (toItems->size() >= MaxSelectedItems) break; - toItems->insert(item, FullItemSel); - } else if (i.value() != FullItemSel) { - *i = FullItemSel; - } - } else { - if (i != toItems->cend()) { - toItems->erase(i); - } - } + int32 fromblock = _dragSelFrom->history()->blocks.indexOf(_dragSelFrom->block()), fromitem = _dragSelFrom->block()->items.indexOf(_dragSelFrom); + int32 toblock = _dragSelTo->history()->blocks.indexOf(_dragSelTo->block()), toitem = _dragSelTo->block()->items.indexOf(_dragSelTo); + if (_migrated) { + if (_dragSelFrom->history() == _migrated) { + if (_dragSelTo->history() == _migrated) { + addSelectionRange(toItems, fromblock, fromitem, toblock, toitem, _migrated); + toblock = -1; + toitem = -1; + } else { + addSelectionRange(toItems, fromblock, fromitem, _migrated->blocks.size() - 1, _migrated->blocks.back()->items.size() - 1, _migrated); } - if (toItems->size() >= MaxSelectedItems) break; + fromblock = 0; fromitem = 0; + } else if (_dragSelTo->history() == _migrated) { // wtf + toblock = -1; + toitem = -1; } } + addSelectionRange(toItems, fromblock, fromitem, toblock, toitem, _history); } else { for (SelectedItems::iterator i = toItems->begin(); i != toItems->cend();) { - int32 iy = i.key()->y + i.key()->block()->y; - if (iy >= fromy && iy < toy) { + int32 iy = itemTop(i.key()); + if (iy < 0) { + if (iy < -1) i = toItems->erase(i); + continue; + } + if (iy >= selfromy && iy < seltoy) { i = toItems->erase(i); } else { ++i; @@ -2363,6 +2554,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _activeAnimMsgId(0) , _scroll(this, st::historyScroll, false) , _list(0) +, _migrated(0) , _history(0) , _histInited(false) , _toHistoryEnd(this, st::historyToEnd) @@ -2547,7 +2739,7 @@ void HistoryWidget::onMentionHashtagOrBotCommandInsert(QString str) { } void HistoryWidget::onTextChange() { - if (_peer && (!_peer->isChannel() || !_peer->asChannel()->canPublish() || (!_peer->asChannel()->isBroadcast() && !_broadcast.checked()))) { + if (_peer && (!_peer->isChannel() || _peer->isMegagroup() || !_peer->asChannel()->canPublish() || (!_peer->asChannel()->isBroadcast() && !_broadcast.checked()))) { updateSendAction(_history, SendActionTyping); } @@ -2572,21 +2764,21 @@ void HistoryWidget::onTextChange() { update(); } - if (!_history || _synthedTextUpdate) return; + if (!_peer || _synthedTextUpdate) return; _saveDraftText = true; onDraftSave(true); } void HistoryWidget::onDraftSaveDelayed() { - if (!_history || _synthedTextUpdate) return; + if (!_peer || _synthedTextUpdate) return; if (!_field.textCursor().anchor() && !_field.textCursor().position() && !_field.verticalScrollBar()->value()) { - if (!Local::hasDraftPositions(_history->peer->id)) return; + if (!Local::hasDraftPositions(_peer->id)) return; } onDraftSave(true); } void HistoryWidget::onDraftSave(bool delayed) { - if (!_history) return; + if (!_peer) return; if (delayed) { uint64 ms = getms(); if (!_saveDraftStart) { @@ -2600,14 +2792,24 @@ void HistoryWidget::onDraftSave(bool delayed) { } void HistoryWidget::writeDraft(MsgId *replyTo, const QString *text, const MessageCursor *cursor, bool *previewCancelled) { - bool save = _history && (_saveDraftStart > 0); + bool save = _peer && (_saveDraftStart > 0); _saveDraftStart = 0; _saveDraftTimer.stop(); if (_saveDraftText) { - if (save) Local::writeDraft(_history->peer->id, Local::MessageDraft(replyTo ? (*replyTo) : _replyToId, text ? (*text) : _field.getLastText(), previewCancelled ? (*previewCancelled) : _previewCancelled)); + if (save) { + Local::writeDraft(_peer->id, Local::MessageDraft(replyTo ? (*replyTo) : _replyToId, text ? (*text) : _field.getLastText(), previewCancelled ? (*previewCancelled) : _previewCancelled)); + if (_migrated) { + Local::writeDraft(_migrated->peer->id, Local::MessageDraft()); + } + } _saveDraftText = false; } - if (save) Local::writeDraftPositions(_history->peer->id, cursor ? (*cursor) : MessageCursor(_field)); + if (save) { + Local::writeDraftPositions(_peer->id, cursor ? (*cursor) : MessageCursor(_field)); + if (_migrated) { + Local::writeDraftPositions(_migrated->peer->id, MessageCursor()); + } + } } void HistoryWidget::cancelSendAction(History *history, SendActionType type) { @@ -2721,7 +2923,7 @@ void HistoryWidget::onRecordUpdate(qint16 level, qint32 samples) { stopRecording(_peer && samples > 0 && _inField); } updateField(); - if (_peer && (!_peer->isChannel() || !_peer->asChannel()->canPublish() || (!_peer->asChannel()->isBroadcast() && !_broadcast.checked()))) { + if (_peer && (!_peer->isChannel() || _peer->isMegagroup() || !_peer->asChannel()->canPublish() || (!_peer->asChannel()->isBroadcast() && !_broadcast.checked()))) { updateSendAction(_history, SendActionRecordAudio); } } @@ -2833,8 +3035,14 @@ void HistoryWidget::clearReplyReturns() { void HistoryWidget::pushReplyReturn(HistoryItem *item) { if (!item) return; + if (item->history() == _history) { + _replyReturns.push_back(item->id); + } else if (item->history() == _migrated) { + _replyReturns.push_back(-item->id); + } else { + return; + } _replyReturn = item; - _replyReturns.push_back(_replyReturn->id); updateControlsVisibility(); } @@ -2846,10 +3054,22 @@ void HistoryWidget::setReplyReturns(PeerId peer, const QList &replyReturn if (!_peer || _peer->id != peer) return; _replyReturns = replyReturns; - _replyReturn = _replyReturns.isEmpty() ? 0 : App::histItemById(_channel, _replyReturns.back()); + if (_replyReturns.isEmpty()) { + _replyReturn = 0; + } else if (_replyReturns.back() < 0 && -_replyReturns.back() < ServerMaxMsgId) { + _replyReturn = App::histItemById(0, -_replyReturns.back()); + } else { + _replyReturn = App::histItemById(_channel, _replyReturns.back()); + } while (!_replyReturns.isEmpty() && !_replyReturn) { _replyReturns.pop_back(); - _replyReturn = _replyReturns.isEmpty() ? 0 : App::histItemById(_channel, _replyReturns.back()); + if (_replyReturns.isEmpty()) { + _replyReturn = 0; + } else if (_replyReturns.back() < 0 && -_replyReturns.back() < ServerMaxMsgId) { + _replyReturn = App::histItemById(0, -_replyReturns.back()); + } else { + _replyReturn = App::histItemById(_channel, _replyReturns.back()); + } } updateControlsVisibility(); } @@ -2858,7 +3078,13 @@ void HistoryWidget::calcNextReplyReturn() { _replyReturn = 0; while (!_replyReturns.isEmpty() && !_replyReturn) { _replyReturns.pop_back(); - _replyReturn = _replyReturns.isEmpty() ? 0 : App::histItemById(_channel, _replyReturns.back()); + if (_replyReturns.isEmpty()) { + _replyReturn = 0; + } else if (_replyReturns.back() < 0 && -_replyReturns.back() < ServerMaxMsgId) { + _replyReturn = App::histItemById(0, -_replyReturns.back()); + } else { + _replyReturn = App::histItemById(_channel, _replyReturns.back()); + } } if (!_replyReturn) updateControlsVisibility(); } @@ -2943,8 +3169,12 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId) { } clearDelayedShowAt(); - if (_replyReturn && _replyReturn->id == showAtMsgId) { - calcNextReplyReturn(); + if (_replyReturn) { + if (_replyReturn->history() == _history && _replyReturn->id == showAtMsgId) { + calcNextReplyReturn(); + } else if (_replyReturn->history() == _migrated && -_replyReturn->id == showAtMsgId) { + calcNextReplyReturn(); + } } _showAtMsgId = showAtMsgId; @@ -2988,7 +3218,10 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId) { if (_history->unreadBar) { _history->unreadBar->destroy(); } - _history = 0; + if (_migrated && _migrated->unreadBar) { + _migrated->unreadBar->destroy(); + } + _history = _migrated = 0; } if (_replyToId) { @@ -3040,9 +3273,8 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId) { _serviceImageCacheSize = imageCacheSize(); MTP::clearLoaderPriorities(); - if (_peer->input.type() == mtpc_inputPeerEmpty) { // maybe should load user - } _history = App::history(_peer->id); + _migrated = _peer->migrateFrom() ? App::history(_peer->migrateFrom()->id) : 0; if (_channel) updateNotifySettings(); @@ -3074,12 +3306,24 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId) { if (_history->draftToId > 0 || !_history->draft.isEmpty()) { applyDraft(false); _replyToId = readyToForward() ? 0 : _history->draftToId; + } else if (_migrated && !_migrated->draft.isEmpty()) { + _history->draft = _migrated->draft; + _history->draftCursor = _migrated->draftCursor; + _history->draftPreviewCancelled = _migrated->draftPreviewCancelled; + _history->draftToId = 0; + applyDraft(false); + _replyToId = 0; } else { + bool fromMigrated = false; Local::MessageDraft draft = Local::readDraft(_peer->id); + if (draft.replyTo <= 0 && draft.text.isEmpty() && _migrated) { + fromMigrated = true; + draft = Local::readDraft(_migrated->peer->id); + } setFieldText(draft.text); _field.setFocus(); if (!draft.text.isEmpty()) { - MessageCursor cur = Local::readDraftPositions(_peer->id); + MessageCursor cur = Local::readDraftPositions(fromMigrated ? _migrated->peer->id : _peer->id); cur.applyTo(_field, &_synthedTextUpdate); } _replyToId = readyToForward() ? 0 : draft.replyTo; @@ -3403,7 +3647,7 @@ void HistoryWidget::updateControlsVisibility() { _field.setPlaceholder(lang(_broadcast.checked() ? lng_broadcast_ph : lng_comment_ph)); } else { _broadcast.hide(); - _field.setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_history->peer->asChannel()->canPublish() ? lng_broadcast_ph : lng_comment_ph) : lng_message_ph)); + _field.setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_peer->asChannel()->canPublish() ? lng_broadcast_ph : lng_comment_ph) : lng_message_ph)); } } if (_replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { @@ -3460,6 +3704,7 @@ void HistoryWidget::newUnreadMsg(History *history, HistoryItem *item) { if (_history == history) { if (_scroll.scrollTop() + 1 > _scroll.scrollTopMax()) { if (history->unreadBar) history->unreadBar->destroy(); + if (_migrated && _migrated->unreadBar) _migrated->unreadBar->destroy(); } } App::wnd()->notifySchedule(history, item); @@ -3476,6 +3721,7 @@ void HistoryWidget::historyToDown(History *history) { void HistoryWidget::historyWasRead(bool force) { App::main()->readServerHistory(_history, force); + if (_migrated) App::main()->readServerHistory(_migrated, force); } void HistoryWidget::historyCleared(History *history) { @@ -3514,6 +3760,12 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages return; } + bool toMigrated = (peer == _peer->migrateFrom()); + if (peer != _peer && !toMigrated) { + _preloadRequest = _preloadDownRequest = _firstLoadRequest = _delayedShowAtRequest = 0; + return; + } + int32 count = 0; const QVector emptyList, *histList = &emptyList; const QVector *histCollapsed = 0; @@ -3548,7 +3800,7 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages } if (_preloadRequest == requestId) { - addMessagesToFront(*histList, histCollapsed); + addMessagesToFront(peer, *histList, histCollapsed); _preloadRequest = 0; onListScroll(); if (_reportSpamStatus == dbiprsUnknown) { @@ -3556,12 +3808,17 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages if (_reportSpamStatus != dbiprsUnknown) updateControlsVisibility(); } } else if (_preloadDownRequest == requestId) { - addMessagesToBack(*histList, histCollapsed); + addMessagesToBack(peer, *histList, histCollapsed); _preloadDownRequest = 0; onListScroll(); if (_history->loadedAtBottom() && App::wnd()) App::wnd()->checkHistoryActivation(); } else if (_firstLoadRequest == requestId) { - addMessagesToFront(*histList, histCollapsed); + if (toMigrated) { + _history->clear(true); + } else if (_migrated) { + _migrated->clear(true); + } + addMessagesToFront(peer, *histList, histCollapsed); if (_fixedInScrollMsgId && _history->isChannel()) { _history->asChannelHistory()->insertCollapseItem(_fixedInScrollMsgId); } @@ -3578,6 +3835,11 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages historyLoaded(); } else if (_delayedShowAtRequest == requestId) { + if (toMigrated) { + _history->clear(true); + } else if (_migrated) { + _migrated->clear(true); + } _delayedShowAtRequest = 0; bool wasOnlyImportant = _history->isChannel() ? _history->asChannelHistory()->onlyImportant() : true; _history->getReadyFor(_delayedShowAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); @@ -3590,7 +3852,7 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages if (_firstLoadRequest) MTP::cancel(_firstLoadRequest); _preloadRequest = _preloadDownRequest = 0; _firstLoadRequest = -1; // hack - don't updateListSize yet - addMessagesToFront(*histList, histCollapsed); + addMessagesToFront(peer, *histList, histCollapsed); if (_fixedInScrollMsgId && _history->isChannel()) { _history->asChannelHistory()->insertCollapseItem(_fixedInScrollMsgId); } @@ -3605,8 +3867,12 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages } } } - if (_replyReturn && _replyReturn->id == _delayedShowAtMsgId) { - calcNextReplyReturn(); + if (_replyReturn) { + if (_replyReturn->history() == _history && _replyReturn->id == _delayedShowAtMsgId) { + calcNextReplyReturn(); + } else if (_replyReturn->history() == _migrated && -_replyReturn->id == _delayedShowAtMsgId) { + calcNextReplyReturn(); + } } setMsgId(_delayedShowAtMsgId); @@ -3626,6 +3892,9 @@ void HistoryWidget::historyLoaded() { if (_history->unreadBar) { _history->unreadBar->destroy(); } + if (_migrated && _migrated->unreadBar) { + _migrated->unreadBar->destroy(); + } doneShow(); } @@ -3638,6 +3907,7 @@ bool HistoryWidget::isActive() const { if (_firstLoadRequest || _a_show.animating()) return false; if (_history->loadedAtBottom()) return true; if (_history->showFrom && !_history->showFrom->detached() && _history->unreadBar) return true; + if (_migrated && _migrated->showFrom && !_migrated->showFrom->detached() && _migrated->unreadBar) return true; return false; } @@ -3646,12 +3916,18 @@ void HistoryWidget::firstLoadMessages() { bool loadImportant = (_history->isChannel() && !_history->isMegagroup()) ? _history->asChannelHistory()->onlyImportant() : false; bool wasOnlyImportant = loadImportant; - int32 from = 0, offset = 0, loadCount = MessagesPerPage; + PeerData *from = _peer; + int32 offset_id = 0, offset = 0, loadCount = MessagesPerPage; if (_showAtMsgId == ShowAtUnreadMsgId) { - if (_history->unreadCount) { + if (_migrated && _migrated->unreadCount) { + _history->getReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); + from = _migrated->peer; + offset = -loadCount / 2; + offset_id = _migrated->inboxReadBefore; + } else if (_history->unreadCount) { _history->getReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); offset = -loadCount / 2; - from = _history->inboxReadBefore; + offset_id = _history->inboxReadBefore; } else { _history->getReadyFor(ShowAtTheEndMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); } @@ -3661,21 +3937,26 @@ void HistoryWidget::firstLoadMessages() { } else if (_showAtMsgId > 0) { _history->getReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); offset = -loadCount / 2; - from = _showAtMsgId; + offset_id = _showAtMsgId; } else if (_showAtMsgId < 0 && _history->isChannel()) { - if (_showAtMsgId == SwitchAtTopMsgId) { + if (_showAtMsgId < 0 && -_showAtMsgId < ServerMaxMsgId && _migrated) { + _history->getReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); + from = _migrated->peer; + offset = -loadCount / 2; + offset_id = -_showAtMsgId; + } else if (_showAtMsgId == SwitchAtTopMsgId) { _history->getReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); loadImportant = true; } else if (HistoryItem *item = App::histItemById(_channel, _showAtMsgId)) { if (item->type() == HistoryItemGroup) { _history->getReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); offset = -loadCount / 2; - from = qMax(static_cast(item)->minId(), 1); + offset_id = qMax(static_cast(item)->minId(), 1); loadImportant = false; } else if (item->type() == HistoryItemCollapse) { _history->getReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); offset = -loadCount / 2; - from = qMax(static_cast(item)->wasMinId(), 1); + offset_id = qMax(static_cast(item)->wasMinId(), 1); loadImportant = true; } } @@ -3691,39 +3972,63 @@ void HistoryWidget::firstLoadMessages() { } if (loadImportant) { - _firstLoadRequest = MTP::send(MTPchannels_GetImportantHistory(_peer->asChannel()->inputChannel, MTP_int(from), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, _peer), rpcFail(&HistoryWidget::messagesFailed)); + _firstLoadRequest = MTP::send(MTPchannels_GetImportantHistory(from->asChannel()->inputChannel, MTP_int(offset_id), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, from), rpcFail(&HistoryWidget::messagesFailed)); } else { - _firstLoadRequest = MTP::send(MTPmessages_GetHistory(_peer->input, MTP_int(from), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, _peer), rpcFail(&HistoryWidget::messagesFailed)); + _firstLoadRequest = MTP::send(MTPmessages_GetHistory(from->input, MTP_int(offset_id), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, from), rpcFail(&HistoryWidget::messagesFailed)); } } void HistoryWidget::loadMessages() { - if (!_history || _history->loadedAtTop() || _preloadRequest) return; + if (!_history || _preloadRequest) return; - bool loadImportant = (_history->isChannel() && !_history->isMegagroup()) ? _history->asChannelHistory()->onlyImportant() : false; - MsgId min = _history->minMsgId(); - int32 offset = 0, loadCount = min ? MessagesPerPage : MessagesFirstLoad; + if (_history->isEmpty() && _migrated && _migrated->isEmpty()) { + return firstLoadMessages(); + } + + bool loadMigrated = _migrated && (_history->isEmpty() || _history->loadedAtTop() || (!_migrated->isEmpty() && !_migrated->loadedAtBottom())); + History *from = loadMigrated ? _migrated : _history; + if (from->loadedAtTop()) { + return; + } + + bool loadImportant = (from->isChannel() && !from->isMegagroup()) ? from->asChannelHistory()->onlyImportant() : false; + MsgId offset_id = from->minMsgId(); + int32 offset = 0, loadCount = offset_id ? MessagesPerPage : MessagesFirstLoad; if (loadImportant) { - _preloadRequest = MTP::send(MTPchannels_GetImportantHistory(_peer->asChannel()->inputChannel, MTP_int(min), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, _peer), rpcFail(&HistoryWidget::messagesFailed)); + _preloadRequest = MTP::send(MTPchannels_GetImportantHistory(from->peer->asChannel()->inputChannel, MTP_int(offset_id), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, from->peer), rpcFail(&HistoryWidget::messagesFailed)); } else { - _preloadRequest = MTP::send(MTPmessages_GetHistory(_peer->input, MTP_int(min), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, _peer), rpcFail(&HistoryWidget::messagesFailed)); + _preloadRequest = MTP::send(MTPmessages_GetHistory(from->peer->input, MTP_int(offset_id), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, from->peer), rpcFail(&HistoryWidget::messagesFailed)); } } void HistoryWidget::loadMessagesDown() { - if (!_history || _history->loadedAtBottom() || _preloadDownRequest) return; + if (!_history || _preloadDownRequest) return; - MsgId max = _history->maxMsgId(); - if (!max) return; + if (_history->isEmpty() && _migrated && _migrated->isEmpty()) { + return firstLoadMessages(); + } + + bool loadMigrated = _migrated && !(_migrated->isEmpty() || _migrated->loadedAtBottom() || (!_history->isEmpty() && !_history->loadedAtTop())); + History *from = loadMigrated ? _migrated : _history; + if (from->loadedAtBottom()) { + return; + } - bool loadImportant = (_history->isChannel() && !_history->isMegagroup()) ? _history->asChannelHistory()->onlyImportant() : false; + bool loadImportant = (from->isChannel() && !from->isMegagroup()) ? from->asChannelHistory()->onlyImportant() : false; int32 loadCount = MessagesPerPage, offset = -loadCount; + MsgId offset_id = from->maxMsgId(); + if (!offset_id) { + if (loadMigrated || !_migrated) return; + ++offset_id; + ++offset; + } + if (loadImportant) { - _preloadDownRequest = MTP::send(MTPchannels_GetImportantHistory(_peer->asChannel()->inputChannel, MTP_int(max + 1), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, _peer), rpcFail(&HistoryWidget::messagesFailed)); + _preloadDownRequest = MTP::send(MTPchannels_GetImportantHistory(from->peer->asChannel()->inputChannel, MTP_int(offset_id + 1), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, from->peer), rpcFail(&HistoryWidget::messagesFailed)); } else { - _preloadDownRequest = MTP::send(MTPmessages_GetHistory(_peer->input, MTP_int(max + 1), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, _peer), rpcFail(&HistoryWidget::messagesFailed)); + _preloadDownRequest = MTP::send(MTPmessages_GetHistory(from->peer->input, MTP_int(offset_id + 1), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, from->peer), rpcFail(&HistoryWidget::messagesFailed)); } } @@ -3734,11 +4039,16 @@ void HistoryWidget::delayedShowAt(MsgId showAtMsgId) { _delayedShowAtMsgId = showAtMsgId; bool loadImportant = (_history->isChannel() && !_history->isMegagroup()) ? _history->asChannelHistory()->onlyImportant() : false; - int32 from = 0, offset = 0, loadCount = MessagesPerPage; + PeerData *from = _peer; + int32 offset_id = 0, offset = 0, loadCount = MessagesPerPage; if (_delayedShowAtMsgId == ShowAtUnreadMsgId) { - if (_history->unreadCount) { + if (_migrated && _migrated->unreadCount) { + from = _migrated->peer; offset = -loadCount / 2; - from = _history->inboxReadBefore; + offset_id = _migrated->inboxReadBefore; + } else if (_history->unreadCount) { + offset = -loadCount / 2; + offset_id = _history->inboxReadBefore; } else { loadCount = MessagesFirstLoad; } @@ -3746,23 +4056,27 @@ void HistoryWidget::delayedShowAt(MsgId showAtMsgId) { loadCount = MessagesFirstLoad; } else if (_delayedShowAtMsgId > 0) { offset = -loadCount / 2; - from = _delayedShowAtMsgId; + offset_id = _delayedShowAtMsgId; if (HistoryItem *item = App::histItemById(_channel, _delayedShowAtMsgId)) { if (!item->isImportant()) { loadImportant = false; } } } else if (_delayedShowAtMsgId < 0 && _history->isChannel()) { - if (_delayedShowAtMsgId == SwitchAtTopMsgId) { + if (_delayedShowAtMsgId < 0 && -_delayedShowAtMsgId < ServerMaxMsgId && _migrated) { + from = _migrated->peer; + offset = -loadCount / 2; + offset_id = -_delayedShowAtMsgId; + } else if (_delayedShowAtMsgId == SwitchAtTopMsgId) { loadImportant = true; } else if (HistoryItem *item = App::histItemById(_channel, _delayedShowAtMsgId)) { if (item->type() == HistoryItemGroup) { offset = -loadCount / 2; - from = qMax(static_cast(item)->minId(), 1); + offset_id = qMax(static_cast(item)->minId(), 1); loadImportant = false; } else if (item->type() == HistoryItemCollapse) { offset = -loadCount / 2; - from = qMax(static_cast(item)->wasMinId(), 1); + offset_id = qMax(static_cast(item)->wasMinId(), 1); loadImportant = true; } } @@ -3772,15 +4086,15 @@ void HistoryWidget::delayedShowAt(MsgId showAtMsgId) { } if (loadImportant) { - _delayedShowAtRequest = MTP::send(MTPchannels_GetImportantHistory(_peer->asChannel()->inputChannel, MTP_int(from), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, _peer), rpcFail(&HistoryWidget::messagesFailed)); + _delayedShowAtRequest = MTP::send(MTPchannels_GetImportantHistory(from->asChannel()->inputChannel, MTP_int(offset_id), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, from), rpcFail(&HistoryWidget::messagesFailed)); } else { - _delayedShowAtRequest = MTP::send(MTPmessages_GetHistory(_peer->input, MTP_int(from), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, _peer), rpcFail(&HistoryWidget::messagesFailed)); + _delayedShowAtRequest = MTP::send(MTPmessages_GetHistory(from->input, MTP_int(offset_id), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, from), rpcFail(&HistoryWidget::messagesFailed)); } } void HistoryWidget::onListScroll() { App::checkImageCacheSize(); - if (_firstLoadRequest || _scroll.isHidden()) return; + if (_firstLoadRequest || _scroll.isHidden() || !_peer) return; updateToEndVisibility(); updateCollapseCommentsVisibility(); @@ -3795,8 +4109,10 @@ void HistoryWidget::onListScroll() { } while (_replyReturn) { - bool below = (_replyReturn->detached() && !_history->isEmpty() && _replyReturn->id < _history->blocks.back()->items.back()->id); - if (!below && !_replyReturn->detached()) below = (st >= stm) || (_replyReturn->y + _replyReturn->block()->y < st + sh / 2); + bool below = (_replyReturn->detached() && _replyReturn->history() == _history && !_history->isEmpty() && _replyReturn->id < _history->blocks.back()->items.back()->id); + if (!below) below = (_replyReturn->detached() && _replyReturn->history() == _migrated && !_history->isEmpty()); + if (!below) below = (_replyReturn->detached() && _migrated && _replyReturn->history() == _migrated && !_migrated->isEmpty() && _replyReturn->id < _migrated->blocks.back()->items.back()->id); + if (!below && !_replyReturn->detached()) below = (st >= stm) || (_list->itemTop(_replyReturn) < st + sh / 2); if (below) { calcNextReplyReturn(); } else { @@ -3810,20 +4126,24 @@ void HistoryWidget::onVisibleChanged() { } void HistoryWidget::onHistoryToEnd() { - if (_replyReturn) { + if (_replyReturn && _replyReturn->history() == _history) { showHistory(_peer->id, _replyReturn->id); + } else if (_replyReturn && _replyReturn->history() == _migrated) { + showHistory(_peer->id, -_replyReturn->id); } else if (_peer) { showHistory(_peer->id, ShowAtUnreadMsgId); } } void HistoryWidget::onCollapseComments() { + if (!_peer) return; + MsgId switchAt = SwitchAtTopMsgId; bool collapseCommentsVisible = !_a_show.animating() && _history && !_firstLoadRequest && _history->isChannel() && !_history->asChannelHistory()->onlyImportant(); if (collapseCommentsVisible) { if (HistoryItem *collapse = _history->asChannelHistory()->collapse()) { if (!collapse->detached()) { - int32 collapseY = (_list->height() - _history->height - st::historyPadding) + collapse->y + collapse->block()->y - _scroll.scrollTop(); + int32 collapseY = _list->itemTop(collapse) - _scroll.scrollTop(); if (collapseY >= 0 && collapseY < _scroll.height()) { switchAt = collapse->id; } @@ -3899,7 +4219,7 @@ void HistoryWidget::onBotStart() { sendBotCommand(qsl("/start"), 0); } else { uint64 randomId = MTP::nonce(); - MTP::send(MTPmessages_StartBot(_peer->asUser()->inputUser, MTP_int(0), MTP_long(randomId), MTP_string(token)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _peer->asUser())); + MTP::send(MTPmessages_StartBot(_peer->asUser()->inputUser, MTP_inputPeerEmpty(), MTP_long(randomId), MTP_string(token)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _peer->asUser())); _peer->asUser()->botInfo->startToken = QString(); if (_keyboard.hasMarkup()) { @@ -4294,7 +4614,7 @@ void HistoryWidget::stopRecording(bool send) { _recording = false; _recordingSamples = 0; - if (_peer && (!_peer->isChannel() || !_peer->asChannel()->canPublish() || (!_peer->asChannel()->isBroadcast() && !_broadcast.checked()))) { + if (_peer && (!_peer->isChannel() || _peer->isMegagroup() || !_peer->asChannel()->canPublish() || (!_peer->asChannel()->isBroadcast() && !_broadcast.checked()))) { updateSendAction(_history, SendActionRecordAudio, -1); } @@ -4452,7 +4772,7 @@ bool HistoryWidget::readyToForward() const { } bool HistoryWidget::hasBroadcastToggle() const { - return _history && _history->peer->isChannel() && !_history->peer->isMegagroup() && _history->peer->asChannel()->canPublish() && !_history->peer->asChannel()->isBroadcast(); + return _peer && _peer->isChannel() && !_peer->isMegagroup() && _peer->asChannel()->canPublish() && !_peer->asChannel()->isBroadcast(); } bool HistoryWidget::isBotStart() const { @@ -4569,7 +4889,7 @@ void HistoryWidget::onKbToggle(bool manual) { _field.setMaxHeight(st::maxFieldHeight); - _kbReplyTo = (_history->peer->isChat() || _history->peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; + _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; if (_kbReplyTo && !_replyToId) { updateReplyToName(); _replyToText.setText(st::msgFont, _kbReplyTo->inDialogsText(), _textDlgOptions); @@ -4585,7 +4905,7 @@ void HistoryWidget::onKbToggle(bool manual) { int32 maxh = qMin(_keyboard.height(), int(st::maxFieldHeight) - (int(st::maxFieldHeight) / 2)); _field.setMaxHeight(st::maxFieldHeight - maxh); - _kbReplyTo = (_history->peer->isChat() || _history->peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; + _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; if (_kbReplyTo && !_replyToId) { updateReplyToName(); _replyToText.setText(st::msgFont, _kbReplyTo->inDialogsText(), _textDlgOptions); @@ -4635,6 +4955,13 @@ void HistoryWidget::selectMessage() { if (_list) _list->selectItem(item); } +void HistoryWidget::onForwardHere() { + HistoryItem *item = App::contextItem(); + if (!item || item->type() != HistoryItemMsg || item->serviceMsg()) return; + + App::forward(_peer->id, ForwardContextMessage); +} + void HistoryWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth) { if (_a_show.animating()) { p.drawPixmap(a_coordUnder.current(), 0, _cacheTopBarUnder); @@ -4659,7 +4986,7 @@ void HistoryWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth) } p.setPen(st::dlgNameColor->p); - _history->peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); + _peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); if (cWideMode()) { p.setOpacity(st::topBarForwardAlpha + (1 - st::topBarForwardAlpha) * over); @@ -4783,9 +5110,9 @@ void HistoryWidget::checkMentionDropdown() { _field.getMentionHashtagBotCommandStart(start); if (!start.isEmpty()) { if (start.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtags(); - if (start.at(0) == '@' && _history->peer->isUser()) return; - if (start.at(0) == '/' && _history->peer->isUser() && !_history->peer->asUser()->botInfo) return; - _attachMention.showFiltered(_history->peer, start); + if (start.at(0) == '@' && _peer->isUser()) return; + if (start.at(0) == '/' && _peer->isUser() && !_peer->asUser()->botInfo) return; + _attachMention.showFiltered(_peer, start); } else if (!_attachMention.isHidden()) { _attachMention.hideStart(); } @@ -4880,7 +5207,7 @@ void HistoryWidget::confirmSendFile(const FileLoadResultPtr &file, bool ctrlShif int32 flags = newMessageFlags(h->peer) | MTPDmessage::flag_media; // unread, out if (file->to.replyTo) flags |= MTPDmessage::flag_reply_to_msg_id; - bool fromChannelName = h->peer->isChannel() && h->peer->asChannel()->canPublish() && (h->peer->asChannel()->isBroadcast() || file->to.broadcast); + bool fromChannelName = h->peer->isChannel() && !h->peer->isMegagroup() && h->peer->asChannel()->canPublish() && (h->peer->asChannel()->isBroadcast() || file->to.broadcast); if (fromChannelName) { flags |= MTPDmessage::flag_views; } else { @@ -4944,7 +5271,7 @@ void HistoryWidget::onPhotoUploaded(const FullMsgId &newId, const MTPInputFile & sendFlags |= MTPmessages_SendMedia::flag_reply_to_msg_id; } - bool fromChannelName = hist->peer->isChannel() && hist->peer->asChannel()->canPublish() && item->fromChannel(); + bool fromChannelName = hist->peer->isChannel() && !hist->peer->isMegagroup() && hist->peer->asChannel()->canPublish() && item->fromChannel(); if (fromChannelName) { sendFlags |= MTPmessages_SendMedia::flag_broadcast; } @@ -5062,7 +5389,7 @@ void HistoryWidget::onPhotoProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadPhoto, 0); } -// msgUpdated(item->history()->peer->id, item); +// msgUpdated(item); } } @@ -5073,7 +5400,7 @@ void HistoryWidget::onDocumentProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadFile, doc ? doc->uploadOffset : 0); } - msgUpdated(item->history()->peer->id, item); + msgUpdated(item); } } @@ -5084,7 +5411,7 @@ void HistoryWidget::onAudioProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadAudio, audio ? audio->uploadOffset : 0); } - msgUpdated(item->history()->peer->id, item); + msgUpdated(item); } } @@ -5095,7 +5422,7 @@ void HistoryWidget::onPhotoFailed(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadPhoto, -1); } -// msgUpdated(item->history()->peer->id, item); +// msgUpdated(item); } } @@ -5106,7 +5433,7 @@ void HistoryWidget::onDocumentFailed(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadFile, -1); } - msgUpdated(item->history()->peer->id, item); + msgUpdated(item); } } @@ -5117,7 +5444,7 @@ void HistoryWidget::onAudioFailed(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadAudio, -1); } - msgUpdated(item->history()->peer->id, item); + msgUpdated(item); } } @@ -5201,8 +5528,8 @@ void HistoryWidget::peerMessagesUpdated() { if (_list) peerMessagesUpdated(_peer->id); } -void HistoryWidget::msgUpdated(PeerId peer, const HistoryItem *msg) { - if (_peer && _list && peer == _peer->id) { +void HistoryWidget::msgUpdated(const HistoryItem *msg) { + if (_peer && _list && (msg->history() == _history || (_migrated && msg->history() == _migrated))) { _list->updateMsg(msg); } } @@ -5342,21 +5669,24 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, _scroll.show(); } _list->updateSize(); - int32 firstItemY = _list->height() - _history->height - st::historyPadding; + int32 historyTop = _list->historyTop(), migratedTop = _list->migratedTop(); if (resizedItem && !resizedItem->detached() && scrollToIt) { - if (newSt + _scroll.height() < firstItemY + resizedItem->block()->y + resizedItem->y + resizedItem->height()) { - newSt = firstItemY + resizedItem->block()->y + resizedItem->y + resizedItem->height() - _scroll.height(); + int32 resizedTop = _list->itemTop(resizedItem); + if (resizedTop >= 0) { + if (newSt + _scroll.height() < resizedTop + resizedItem->height()) { + newSt = resizedTop + resizedItem->height() - _scroll.height(); + } + if (newSt > resizedTop) { + newSt = resizedTop; + } + wasAtBottom = false; } - if (newSt > firstItemY + resizedItem->block()->y + resizedItem->y) { - newSt = firstItemY + resizedItem->block()->y + resizedItem->y; - } - wasAtBottom = false; } if (washidden) { _scroll.hide(); } - if ((!initial && !wasAtBottom) || loadedDown) { + if ((!initial && !wasAtBottom) || (loadedDown && (!_history->showFrom || _history->unreadBar || _history->loadedAtBottom()) && (!_migrated || !_migrated->showFrom || _migrated->unreadBar || _history->loadedAtBottom()))) { _scroll.scrollToY(newSt + addToY); return; } @@ -5369,21 +5699,39 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, if (initial && _history->lastWidth) { toY = newSt; _history->lastWidth = 0; - } else if (initial && _showAtMsgId > 0) { - HistoryItem *item = App::histItemById(_channel, _showAtMsgId); - if (!item || item->detached()) { + } else if (initial && _migrated && _showAtMsgId < 0 && -_showAtMsgId < ServerMaxMsgId) { + HistoryItem *item = App::histItemById(0, -_showAtMsgId); + int32 iy = _list->itemTop(item); + if (iy < 0) { setMsgId(0); _histInited = false; return updateListSize(addToY, initial); } else { - toY = (_scroll.height() > item->height()) ? qMax(firstItemY + item->y + item->block()->y - (_scroll.height() - item->height()) / 2, 0) : (firstItemY + item->y + item->block()->y); + toY = (_scroll.height() > item->height()) ? qMax(iy - (_scroll.height() - item->height()) / 2, 0) : iy; _animActiveStart = getms(); _animActiveTimer.start(AnimationTimerDelta); _activeAnimMsgId = _showAtMsgId; } + } else if (initial && _showAtMsgId > 0) { + HistoryItem *item = App::histItemById(_channel, _showAtMsgId); + int32 iy = _list->itemTop(item); + if (iy < 0) { + setMsgId(0); + _histInited = false; + return updateListSize(addToY, initial); + } else { + toY = (_scroll.height() > item->height()) ? qMax(iy - (_scroll.height() - item->height()) / 2, 0) : iy; + _animActiveStart = getms(); + _animActiveTimer.start(AnimationTimerDelta); + _activeAnimMsgId = _showAtMsgId; + if (item->isGroupMigrate() && _migrated && _migrated->loadedAtBottom() && _migrated->blocks.back()->items.back()->isGroupMigrate() && _list->historyTop() != _list->historyDrawTop()) { + _activeAnimMsgId = -_migrated->blocks.back()->items.back()->id; + } + } } else if (initial && _fixedInScrollMsgId > 0) { HistoryItem *item = App::histItemById(_channel, _fixedInScrollMsgId); - if (!item || item->detached()) { + int32 iy = _list->itemTop(item); + if (iy < 0) { item = 0; for (int32 blockIndex = 0, blocksCount = _history->blocks.size(); blockIndex < blocksCount; ++blockIndex) { HistoryBlock *block = _history->blocks.at(blockIndex); @@ -5400,8 +5748,9 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, } } } - if (item) { - toY = qMax(firstItemY + item->y + item->block()->y - _fixedInScrollMsgTop, 0); + iy = _list->itemTop(item); + if (iy >= 0) { + toY = qMax(iy - _fixedInScrollMsgTop, 0); } else { setMsgId(ShowAtUnreadMsgId); _fixedInScrollMsgId = 0; @@ -5410,18 +5759,34 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, return updateListSize(addToY, initial); } } else { - toY = qMax(firstItemY + item->y + item->block()->y + item->height() - _fixedInScrollMsgTop, 0); + toY = qMax(iy + item->height() - _fixedInScrollMsgTop, 0); } + } else if (initial && _migrated && _migrated->unreadBar) { + toY = _list->itemTop(_migrated->unreadBar); } else if (initial && _history->unreadBar) { - toY = firstItemY + _history->unreadBar->y + _history->unreadBar->block()->y; + toY = _list->itemTop(_history->unreadBar); + } else if (_migrated && _migrated->showFrom) { + toY = _list->itemTop(_migrated->showFrom); + if (toY < _scroll.scrollTopMax() + st::unreadBarHeight) { + _migrated->addUnreadBar(); + if (_migrated->unreadBar) { + setMsgId(ShowAtUnreadMsgId); + _histInited = false; + updateListSize(0, true); + App::wnd()->checkHistoryActivation(); + return; + } + } } else if (_history->showFrom) { - toY = firstItemY + _history->showFrom->y + _history->showFrom->block()->y; + toY = _list->itemTop(_history->showFrom); if (toY < _scroll.scrollTopMax() + st::unreadBarHeight) { _history->addUnreadBar(); if (_history->unreadBar) { setMsgId(ShowAtUnreadMsgId); _histInited = false; - return updateListSize(0, true); + updateListSize(0, true); + App::wnd()->checkHistoryActivation(); + return; } } } else { @@ -5429,24 +5794,27 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, _scroll.scrollToY(toY); } -void HistoryWidget::addMessagesToFront(const QVector &messages, const QVector *collapsed) { - int32 oldH = _history->height; - _list->messagesReceived(messages, collapsed); +void HistoryWidget::addMessagesToFront(PeerData *peer, const QVector &messages, const QVector *collapsed) { + int32 oldH = _list->historyHeight(); + _list->messagesReceived(peer, messages, collapsed); if (!_firstLoadRequest) { - updateListSize(_history->height - oldH); + updateListSize(_list->historyHeight() - oldH); updateBotKeyboard(); } } -void HistoryWidget::addMessagesToBack(const QVector &messages, const QVector *collapsed) { - _list->messagesReceivedDown(messages, collapsed); +void HistoryWidget::addMessagesToBack(PeerData *peer, const QVector &messages, const QVector *collapsed) { + _list->messagesReceivedDown(peer, messages, collapsed); if (!_firstLoadRequest) { updateListSize(0, false, true); } } void HistoryWidget::countHistoryShowFrom() { - if (_showAtMsgId != ShowAtUnreadMsgId || !_history->unreadCount) { + if (_migrated && _showAtMsgId == ShowAtUnreadMsgId && _migrated->unreadCount) { + _migrated->updateShowFrom(); + } + if ((_migrated && _migrated->showFrom) || _showAtMsgId != ShowAtUnreadMsgId || !_history->unreadCount) { _history->showFrom = 0; return; } @@ -5486,7 +5854,7 @@ void HistoryWidget::updateBotKeyboard() { int32 maxh = hasMarkup ? qMin(_keyboard.height(), int(st::maxFieldHeight) - (int(st::maxFieldHeight) / 2)) : 0; _field.setMaxHeight(st::maxFieldHeight - maxh); _kbShown = hasMarkup; - _kbReplyTo = (_history->peer->isChat() || _history->peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; + _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; if (_kbReplyTo && !_replyToId) { updateReplyToName(); _replyToText.setText(st::msgFont, _kbReplyTo->inDialogsText(), _textDlgOptions); @@ -5541,7 +5909,7 @@ void HistoryWidget::updateCollapseCommentsVisibility() { if (collapseCommentsVisible) { if (HistoryItem *collapse = _history->asChannelHistory()->collapse()) { if (!collapse->detached()) { - int32 collapseY = (_list->height() - _history->height - st::historyPadding) + collapse->y + collapse->block()->y - _scroll.scrollTop(); + int32 collapseY = _list->itemTop(collapse) - _scroll.scrollTop(); if (collapseY > _scroll.height()) { collapseCommentsTop += qMin(collapseY - _scroll.height() - collapse->height(), 0); } else { @@ -5635,7 +6003,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { } if (p) App::main()->showPeerHistory(p->id, m); } else if (_history && (e->key() == Qt::Key_Search || e == QKeySequence::Find)) { - App::main()->searchInPeer(_history->peer); + App::main()->searchInPeer(_peer); } else { e->ignore(); } @@ -5666,7 +6034,7 @@ void HistoryWidget::onStickerSend(DocumentData *sticker) { flags |= MTPDmessage::flag_reply_to_msg_id; sendFlags |= MTPmessages_SendMedia::flag_reply_to_msg_id; } - bool fromChannelName = _history->peer->isChannel() && !_history->peer->isMegagroup() && _history->peer->asChannel()->canPublish() && (_history->peer->asChannel()->isBroadcast() || _broadcast.checked()); + bool fromChannelName = _peer->isChannel() && !_peer->isMegagroup() && _peer->asChannel()->canPublish() && (_peer->asChannel()->isBroadcast() || _broadcast.checked()); if (fromChannelName) { sendFlags |= MTPmessages_SendMedia::flag_broadcast; } else { @@ -5707,6 +6075,24 @@ void HistoryWidget::onReplyToMessage() { HistoryItem *to = App::contextItem(); if (!to || to->id <= 0 || !_canSendMessages) return; + if (to->history() == _migrated) { + if (to->isGroupMigrate() && _history->blocks.size() > 1 && _history->blocks.at(1)->items.front()->isGroupMigrate() && _history != _migrated) { + App::contextItem(_history->blocks.at(1)->items.front()); + onReplyToMessage(); + App::contextItem(to); + } else { + LayeredWidget *box = 0; + if (to->type() != HistoryItemMsg || to->serviceMsg()) { + box = new InformBox(lang(lng_reply_cant)); + } else { + box = new ConfirmBox(lang(lng_reply_cant_forward), lang(lng_selected_forward)); + connect(box, SIGNAL(confirmed()), this, SLOT(onForwardHere())); + } + App::showLayer(box); + } + return; + } + App::main()->cancelForwarding(); _replyTo = to; @@ -5946,6 +6332,11 @@ void HistoryWidget::onFullPeerUpdated(PeerData *data) { void HistoryWidget::peerUpdated(PeerData *data) { if (data && data == _peer) { + if (data->migrateTo()) { + App::main()->showPeerHistory(data->migrateTo()->id, ShowAtUnreadMsgId); + QTimer::singleShot(ReloadChannelMembersTimeout, App::api(), SLOT(delayedRequestParticipantsCount())); + return; + } updateListSize(); if (_peer->isChannel()) updateReportSpamStatus(); if (App::api()) { @@ -5994,10 +6385,10 @@ void HistoryWidget::onDeleteSelectedSure() { _list->fillSelectedItems(sel); if (sel.isEmpty()) return; - QVector ids; + QMap > ids; for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) { if (i.value()->id > 0) { - ids.push_back(MTP_int(i.value()->id)); + ids[i.value()->history()->peer].push_back(MTP_int(i.value()->id)); } } @@ -6010,8 +6401,8 @@ void HistoryWidget::onDeleteSelectedSure() { } App::wnd()->hideLayer(); - if (!ids.isEmpty()) { - App::main()->deleteMessages(_peer, ids); + for (QMap >::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) { + App::main()->deleteMessages(i.key(), i.value()); } } @@ -6029,7 +6420,7 @@ void HistoryWidget::onDeleteContextSure() { App::main()->checkPeerHistory(h->peer); } - if (App::main() && App::main()->peer() == peer()) { + if (App::main() && (App::main()->peer() == h->peer || (App::main()->peer() && h->peer->migrateTo() == App::main()->peer()))) { App::main()->itemResized(0); } App::wnd()->hideLayer(); @@ -6052,20 +6443,26 @@ void HistoryWidget::onClearSelected() { } void HistoryWidget::onAnimActiveStep() { - if (!_history || _activeAnimMsgId <= 0) return _animActiveTimer.stop(); + if (!_history || !_activeAnimMsgId || (_activeAnimMsgId < 0 && (!_migrated || -_activeAnimMsgId >= ServerMaxMsgId))) { + return _animActiveTimer.stop(); + } - HistoryItem *item = App::histItemById(_channel, _activeAnimMsgId); + HistoryItem *item = (_activeAnimMsgId > 0) ? App::histItemById(_channel, _activeAnimMsgId) : App::histItemById(_migrated->channelId(), -_activeAnimMsgId); if (!item || item->detached()) return _animActiveTimer.stop(); if (getms() - _animActiveStart > st::activeFadeInDuration + st::activeFadeOutDuration) { stopAnimActive(); } else { - App::main()->msgUpdated(_peer->id, item); + App::main()->msgUpdated(item); } } -uint64 HistoryWidget::animActiveTime(MsgId id) const { - return (id == _activeAnimMsgId && _animActiveTimer.isActive()) ? (getms() - _animActiveStart) : 0; +uint64 HistoryWidget::animActiveTime(const HistoryItem *msg) const { + if (!msg) return 0; + if ((msg->history() == _history && msg->id == _activeAnimMsgId) || (_migrated && msg->history() == _migrated && msg->id == -_activeAnimMsgId)) { + return _animActiveTimer.isActive() ? (getms() - _animActiveStart) : 0; + } + return 0; } void HistoryWidget::stopAnimActive() { diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 4be2749ad..87ec9b201 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -40,8 +40,8 @@ public: HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, History *history); - void messagesReceived(const QVector &messages, const QVector *collapsed); - void messagesReceivedDown(const QVector &messages, const QVector *collapsed); + void messagesReceived(PeerData *peer, const QVector &messages, const QVector *collapsed); + void messagesReceivedDown(PeerData *peer, const QVector &messages, const QVector *collapsed); bool event(QEvent *e); // calls touchEvent when necessary void touchEvent(QTouchEvent *e); @@ -89,6 +89,12 @@ public: HistoryItem *atTopImportantMsg(int32 top, int32 height, int32 &bottomUnderScrollTop) const; + int32 historyHeight() const; + int32 migratedTop() const; + int32 historyTop() const; + int32 historyDrawTop() const; + int32 itemTop(const HistoryItem *item) const; // -1 if should not be visible, -2 if bad history() + ~HistoryInner(); public slots: @@ -121,30 +127,34 @@ private: void touchDeaccelerate(int32 elapsed); void adjustCurrent(int32 y) const; + void adjustCurrent(int32 y, History *history) const; HistoryItem *prevItem(HistoryItem *item); HistoryItem *nextItem(HistoryItem *item); void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting, bool force = false); - History *hist; + PeerData *_peer; + History *_migrated, *_history; + int32 _historyOffset, _historySkipHeight; // height of first date and first sys msg - int32 ySkip; - BotInfo *botInfo; - int32 botDescWidth, botDescHeight; - QRect botDescRect; + BotInfo *_botInfo; + int32 _botDescWidth, _botDescHeight; + QRect _botDescRect; - HistoryWidget *historyWidget; - ScrollArea *scrollArea; - mutable int32 currentBlock, currentItem; + HistoryWidget *_widget; + ScrollArea *_scroll; + mutable History *_curHistory; + mutable int32 _curBlock, _curItem; bool _firstLoading; - QTimer linkTipTimer; + QTimer _tooltipTimer; Qt::CursorShape _cursor; typedef QMap SelectedItems; SelectedItems _selected; void applyDragSelection(); void applyDragSelection(SelectedItems *toItems) const; + void addSelectionRange(SelectedItems *toItems, int32 fromblock, int32 fromitem, int32 toblock, int32 toitem, History *h) const; enum DragAction { NoDrag = 0x00, @@ -414,7 +424,7 @@ public: void peerMessagesUpdated(PeerId peer); void peerMessagesUpdated(); - void msgUpdated(PeerId peer, const HistoryItem *msg); + void msgUpdated(const HistoryItem *msg); void newUnreadMsg(History *history, HistoryItem *item); void historyToDown(History *history); void historyWasRead(bool force = true); @@ -471,7 +481,7 @@ public: bool touchScroll(const QPoint &delta); - uint64 animActiveTime(MsgId id) const; + uint64 animActiveTime(const HistoryItem *msg) const; void stopAnimActive(); void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true); @@ -620,6 +630,8 @@ public slots: void forwardMessage(); void selectMessage(); + void onForwardHere(); // instead of a reply + void onFieldFocused(); void onFieldResize(); void onFieldCursorChanged(); @@ -677,8 +689,8 @@ private: bool messagesFailed(const RPCError &error, mtpRequestId requestId); void updateListSize(int32 addToY = 0, bool initial = false, bool loadedDown = false, HistoryItem *resizedItem = 0, bool scrollToIt = false); - void addMessagesToFront(const QVector &messages, const QVector *collapsed); - void addMessagesToBack(const QVector &messages, const QVector *collapsed); + void addMessagesToFront(PeerData *peer, const QVector &messages, const QVector *collapsed); + void addMessagesToBack(PeerData *peer, const QVector &messages, const QVector *collapsed); void reportSpamDone(PeerData *peer, const MTPBool &result, mtpRequestId request); bool reportSpamFail(const RPCError &error, mtpRequestId request); @@ -722,7 +734,7 @@ private: ScrollArea _scroll; HistoryInner *_list; - History *_history; + History *_migrated, *_history; bool _histInited; // initial updateListSize() called IconedButton _toHistoryEnd; diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 7a49d043b..30c35cc11 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -2126,7 +2126,7 @@ namespace Local { void writeDraftPositions(const PeerId &peer, const MessageCursor &cur) { if (!_working()) return; - if (cur.position == 0 && cur.anchor == 0 && cur.scroll == 0) { + if (cur.position == 0 && cur.anchor == 0 && cur.scroll == QFIXED_MAX) { DraftsMap::iterator i = _draftsPositionsMap.find(peer); if (i != _draftsPositionsMap.cend()) { clearKey(i.value()); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index c71064c44..6b6f86d06 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -498,7 +498,7 @@ bool MainWidget::onForward(const PeerId &peer, ForwardWhatMessages what) { } else if (what == ForwardPressedLinkMessage) { item = App::pressedLinkItem(); } - if (dynamic_cast(item) && item->id > 0) { + if (item && item->toHistoryMessage() && item->id > 0) { _toForward.insert(item->id, item); } } @@ -604,9 +604,11 @@ void MainWidget::finishForwarding(History *hist, bool broadcast) { bool fromChannelName = hist->peer->isChannel() && !hist->peer->isMegagroup() && hist->peer->asChannel()->canPublish() && (hist->peer->asChannel()->isBroadcast() || broadcast); if (!_toForward.isEmpty()) { bool genClientSideMessage = (_toForward.size() < 2); - PeerData *forwardFrom = _toForward.cbegin().value()->history()->peer; + PeerData *forwardFrom = 0; App::main()->readServerHistory(hist, false); + int32 flags = fromChannelName ? MTPmessages_ForwardMessages::flag_broadcast : 0; + QVector ids; QVector randomIds; ids.reserve(_toForward.size()); @@ -622,13 +624,22 @@ void MainWidget::finishForwarding(History *hist, bool broadcast) { } App::historyRegRandom(randomId, newId); } - ids.push_back(MTP_int(i.key())); + if (forwardFrom != i.value()->history()->peer) { + if (forwardFrom) { + hist->sendRequestId = MTP::send(MTPmessages_ForwardMessages(MTP_int(flags), forwardFrom->input, MTP_vector(ids), MTP_vector(randomIds), hist->peer->input), rpcDone(&MainWidget::sentUpdatesReceived), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); + ids.resize(0); + randomIds.resize(0); + } + forwardFrom = i.value()->history()->peer; + } + ids.push_back(MTP_int(i.value()->id)); randomIds.push_back(MTP_long(randomId)); } - int32 flags = fromChannelName ? MTPmessages_ForwardMessages::flag_broadcast : 0; hist->sendRequestId = MTP::send(MTPmessages_ForwardMessages(MTP_int(flags), forwardFrom->input, MTP_vector(ids), MTP_vector(randomIds), hist->peer->input), rpcDone(&MainWidget::sentUpdatesReceived), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); - if (history.peer() == hist->peer) history.peerMessagesUpdated(); + if (history.peer() == hist->peer) { + history.peerMessagesUpdated(); + } cancelForwarding(); } @@ -894,12 +905,16 @@ void MainWidget::deletedContact(UserData *user, const MTPcontacts_Link &result) App::emitPeerUpdated(); } +void MainWidget::removeDialog(History *history) { + dialogs.removeDialog(history); +} + void MainWidget::deleteConversation(PeerData *peer, bool deleteHistory) { if (activePeer() == peer) { showDialogs(); } - dialogs.removePeer(peer); if (History *h = App::historyLoaded(peer->id)) { + removeDialog(h); h->clear(); h->newLoaded = true; h->oldLoaded = deleteHistory; @@ -1250,8 +1265,8 @@ void MainWidget::readServerHistory(History *hist, bool force) { } } -uint64 MainWidget::animActiveTime(MsgId id) const { - return history.animActiveTime(id); +uint64 MainWidget::animActiveTime(const HistoryItem *msg) const { + return history.animActiveTime(msg); } void MainWidget::stopAnimActive() { @@ -1406,18 +1421,24 @@ void MainWidget::changingMsgId(HistoryItem *row, MsgId newId) { void MainWidget::itemRemoved(HistoryItem *item) { api()->itemRemoved(item); dialogs.itemRemoved(item); - if (history.peer() == item->history()->peer) { + if (history.peer() == item->history()->peer || (history.peer() && history.peer() == item->history()->peer->migrateTo())) { history.itemRemoved(item); } - if (overview && overview->peer() == item->history()->peer) { + if (overview && (overview->peer() == item->history()->peer || (overview->peer() && overview->peer() == item->history()->peer->migrateTo()))) { overview->itemRemoved(item); } itemRemovedGif(item); if (!_toForward.isEmpty()) { SelectedItemSet::iterator i = _toForward.find(item->id); - if (i != _toForward.end()) { + if (i != _toForward.cend() && i.value() == item) { _toForward.erase(i); updateForwardingTexts(); + } else { + i = _toForward.find(item->id - ServerMaxMsgId); + if (i != _toForward.cend() && i.value() == item) { + _toForward.erase(i); + updateForwardingTexts(); + } } } } @@ -1425,31 +1446,36 @@ void MainWidget::itemRemoved(HistoryItem *item) { void MainWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) { api()->itemReplaced(oldItem, newItem); dialogs.itemReplaced(oldItem, newItem); - if (history.peer() == newItem->history()->peer) { + if (history.peer() == newItem->history()->peer || (history.peer() && history.peer() == newItem->history()->peer->migrateTo())) { history.itemReplaced(oldItem, newItem); } itemReplacedGif(oldItem, newItem); if (!_toForward.isEmpty()) { SelectedItemSet::iterator i = _toForward.find(oldItem->id); - if (i != _toForward.end()) { + if (i != _toForward.cend() && i.value() == oldItem) { i.value() = newItem; + } else { + i = _toForward.find(oldItem->id - ServerMaxMsgId); + if (i != _toForward.cend() && i.value() == oldItem) { + i.value() = newItem; + } } } } void MainWidget::itemResized(HistoryItem *row, bool scrollToIt) { - if (!row || (history.peer() == row->history()->peer && !row->detached())) { + if (!row || ((history.peer() == row->history()->peer || (history.peer() && history.peer() == row->history()->peer->migrateTo())) && !row->detached())) { history.itemResized(row, scrollToIt); } else if (row) { row->history()->width = 0; - if (history.peer() == row->history()->peer) { + if (history.peer() == row->history()->peer || (history.peer() && history.peer() == row->history()->peer->migrateTo())) { history.resizeEvent(0); } } if (overview) { overview->itemResized(row, scrollToIt); } - if (row) msgUpdated(row->history()->peer->id, row); + if (row) msgUpdated(row); } bool MainWidget::overviewFailed(PeerData *peer, const RPCError &error, mtpRequestId req) { @@ -1660,7 +1686,7 @@ void MainWidget::videoLoadProgress(mtpFileLoader *loader) { VideoItems::const_iterator i = items.constFind(video); if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - msgUpdated(j.key()->history()->peer->id, j.key()); + msgUpdated(j.key()); } } } @@ -1723,7 +1749,7 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) { AudioItems::const_iterator i = items.constFind(audio); if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - msgUpdated(j.key()->history()->peer->id, j.key()); + msgUpdated(j.key()); } } } @@ -1758,7 +1784,7 @@ void MainWidget::audioPlayProgress(const AudioMsgId &audioId) { } if (HistoryItem *item = App::histItemById(audioId.msgId)) { - msgUpdated(item->history()->peer->id, item); + msgUpdated(item); } } @@ -1819,7 +1845,7 @@ void MainWidget::documentPlayProgress(const SongMsgId &songId) { } if (HistoryItem *item = App::histItemById(songId.msgId)) { - msgUpdated(item->history()->peer->id, item); + msgUpdated(item); } } @@ -1899,7 +1925,7 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { DocumentItems::const_iterator i = items.constFind(document); if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - msgUpdated(j.key()->history()->peer->id, j.key()); + msgUpdated(j.key()); } } App::wnd()->documentUpdated(document); @@ -2287,6 +2313,12 @@ void MainWidget::ctrlEnterSubmitUpdated() { } void MainWidget::showPeerHistory(quint64 peerId, qint32 showAtMsgId, bool back) { + if (PeerData *peer = App::peerLoaded(peerId)) { + if (peer->migrateTo()) { + peerId = peer->migrateTo()->id; + if (showAtMsgId > 0) showAtMsgId = -showAtMsgId; + } + } if (!back && (!peerId || (_stack.size() == 1 && _stack[0]->type() == HistoryStackItem && _stack[0]->peer->id == peerId))) { back = true; } @@ -2432,6 +2464,10 @@ bool MainWidget::mediaTypeSwitch() { } void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool back, int32 lastScrollTop) { + if (peer->migrateTo()) { + peer = peer->migrateTo(); + } + App::wnd()->hideSettings(); if (overview && overview->peer() == peer) { if (overview->type() != type) { @@ -2497,6 +2533,10 @@ void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool } void MainWidget::showPeerProfile(PeerData *peer, bool back, int32 lastScrollTop) { + if (peer->migrateTo()) { + peer = peer->migrateTo(); + } + App::wnd()->hideSettings(); if (profile && profile->peer() == peer) return; @@ -2601,7 +2641,11 @@ void MainWidget::dlgUpdated(DialogRow *row) { void MainWidget::dlgUpdated(History *row, MsgId msgId) { if (!row) return; - dialogs.dlgUpdated(row, msgId); + if (msgId < 0 && -msgId < ServerMaxMsgId && row->peer->migrateFrom()) { + dialogs.dlgUpdated(App::history(row->peer->migrateFrom()->id), -msgId); + } else { + dialogs.dlgUpdated(row, msgId); + } } void MainWidget::windowShown() { @@ -2624,11 +2668,13 @@ void MainWidget::onActiveChannelUpdateFull() { } } -void MainWidget::msgUpdated(PeerId peer, const HistoryItem *msg) { +void MainWidget::msgUpdated(const HistoryItem *msg) { if (!msg) return; - history.msgUpdated(peer, msg); - if (!msg->history()->dialogs.isEmpty() && msg->history()->lastMsg == msg) dialogs.dlgUpdated(msg->history()->dialogs[0]); - if (overview) overview->msgUpdated(peer, msg); + history.msgUpdated(msg); + if (!msg->history()->dialogs.isEmpty() && msg->history()->lastMsg == msg) { + dialogs.dlgUpdated(msg->history()->dialogs[0]); + } + if (overview) overview->msgUpdated(msg); } void MainWidget::historyToDown(History *hist) { @@ -2929,6 +2975,7 @@ void MainWidget::onHistoryShown(History *history, MsgId atMsgId) { if (_a_show.animating()) { _topBar.hide(); } + dlgUpdated(history, atMsgId); } @@ -4234,7 +4281,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { msgRow->history()->unregTyping(App::self()); } if (!App::historyRegItem(msgRow)) { - msgUpdated(h->peer->id, msgRow); + msgUpdated(msgRow); } else { bool wasLast = (h->lastMsg == msgRow); msgRow->destroy(); @@ -4262,7 +4309,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { if (HistoryItem *item = App::histItemById(NoChannel, v.at(i).v)) { if (item->isMediaUnread()) { item->markMediaRead(); - msgUpdated(item->history()->peer->id, item); + msgUpdated(item); if (item->out() && item->history()->peer->isUser()) { item->history()->peer->asUser()->madeAction(); } @@ -4525,7 +4572,6 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { const MTPDupdateServiceNotification &d(update.c_updateServiceNotification()); if (mtpIsTrue(d.vpopup)) { App::wnd()->showLayer(new InformBox(qs(d.vmessage))); - App::wnd()->serviceNotification(qs(d.vmessage), d.vmedia); } else { App::wnd()->serviceNotification(qs(d.vmessage), d.vmedia); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index d3cfdcfa7..dcfa94523 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -179,13 +179,6 @@ public: } }; -enum ForwardWhatMessages { - ForwardSelectedMessages, - ForwardContextMessage, - ForwardPressedMessage, - ForwardPressedLinkMessage -}; - class MainWidget : public TWidget, public RPCSender { Q_OBJECT @@ -231,6 +224,7 @@ public: void activate(); void createDialog(History *history); + void removeDialog(History *history); void dlgUpdated(DialogRow *row = 0); void dlgUpdated(History *row, MsgId msgId); @@ -241,7 +235,7 @@ public: return sentUpdatesReceived(0, updates); } void inviteToChannelDone(ChannelData *channel, const MTPUpdates &updates); - void msgUpdated(PeerId peer, const HistoryItem *msg); + void msgUpdated(const HistoryItem *msg); void historyToDown(History *hist); void dialogsToUp(); void newUnreadMsg(History *history, HistoryItem *item); @@ -332,7 +326,7 @@ public: void readServerHistory(History *history, bool force = true); - uint64 animActiveTime(MsgId id) const; + uint64 animActiveTime(const HistoryItem *msg) const; void stopAnimActive(); void sendBotCommand(const QString &cmd, MsgId msgId); diff --git a/Telegram/SourceFiles/mtproto/mtpCoreTypes.h b/Telegram/SourceFiles/mtproto/mtpCoreTypes.h index 18cc85396..448302ad2 100644 --- a/Telegram/SourceFiles/mtproto/mtpCoreTypes.h +++ b/Telegram/SourceFiles/mtproto/mtpCoreTypes.h @@ -982,6 +982,7 @@ inline bool mtpIsFalse(const MTPBool &v) { enum { // client side flags MTPDmessage_flag_HAS_TEXT_LINKS = (1 << 31), // message has links for "shared links" indexing + MTPDmessage_flag_IS_GROUP_MIGRATE = (1 << 30), // message is a group migrate (group -> supergroup) service message MTPDreplyKeyboardMarkup_flag_FORCE_REPLY = (1 << 30), // markup just wants a text reply MTPDreplyKeyboardMarkup_flag_ZERO = (1 << 31), // none (zero) markup MTPDstickerSet_flag_NOT_LOADED = (1 << 31), // sticker set is not yet loaded diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.cpp b/Telegram/SourceFiles/mtproto/mtpScheme.cpp index 686e334b2..f004b9cd1 100644 --- a/Telegram/SourceFiles/mtproto/mtpScheme.cpp +++ b/Telegram/SourceFiles/mtproto/mtpScheme.cpp @@ -1664,7 +1664,7 @@ void _serialize_messageActionChatAddUser(MTPStringLogger &to, int32 stage, int32 to.add("\n").addSpaces(lev); } switch (stage) { - case 0: to.add(" user_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 0: to.add(" users: "); ++stages.back(); types.push_back(0); vtypes.push_back(mtpc_int); stages.push_back(0); flags.push_back(0); break; default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; } } @@ -1721,14 +1721,6 @@ void _serialize_messageActionChatMigrateTo(MTPStringLogger &to, int32 stage, int } } -void _serialize_messageActionChatDeactivate(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { - to.add("{ messageActionChatDeactivate }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); -} - -void _serialize_messageActionChatActivate(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { - to.add("{ messageActionChatActivate }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); -} - void _serialize_messageActionChannelMigrateFrom(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { if (stage) { to.add(",\n").addSpaces(lev); @@ -6135,6 +6127,23 @@ void _serialize_messages_search(MTPStringLogger &to, int32 stage, int32 lev, Typ } } +void _serialize_messages_searchGlobal(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ messages_searchGlobal"); + to.add("\n").addSpaces(lev); + } + switch (stage) { + case 0: to.add(" q: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 1: to.add(" offset_date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 2: to.add(" offset_peer: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 3: to.add(" offset_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 4: to.add(" limit: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; + } +} + void _serialize_channels_getImportantHistory(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { if (stage) { to.add(",\n").addSpaces(lev); @@ -6472,7 +6481,7 @@ void _serialize_messages_startBot(MTPStringLogger &to, int32 stage, int32 lev, T } switch (stage) { case 0: to.add(" bot: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 1: to.add(" chat_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 1: to.add(" peer: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 2: to.add(" random_id: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 3: to.add(" start_param: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; @@ -6493,20 +6502,6 @@ void _serialize_messages_toggleChatAdmins(MTPStringLogger &to, int32 stage, int3 } } -void _serialize_messages_deactivateChat(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { - if (stage) { - to.add(",\n").addSpaces(lev); - } else { - to.add("{ messages_deactivateChat"); - to.add("\n").addSpaces(lev); - } - switch (stage) { - case 0: to.add(" chat_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 1: to.add(" enabled: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; - } -} - void _serialize_messages_migrateChat(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { if (stage) { to.add(",\n").addSpaces(lev); @@ -7277,8 +7272,6 @@ namespace { _serializers.insert(mtpc_messageActionChatJoinedByLink, _serialize_messageActionChatJoinedByLink); _serializers.insert(mtpc_messageActionChannelCreate, _serialize_messageActionChannelCreate); _serializers.insert(mtpc_messageActionChatMigrateTo, _serialize_messageActionChatMigrateTo); - _serializers.insert(mtpc_messageActionChatDeactivate, _serialize_messageActionChatDeactivate); - _serializers.insert(mtpc_messageActionChatActivate, _serialize_messageActionChatActivate); _serializers.insert(mtpc_messageActionChannelMigrateFrom, _serialize_messageActionChannelMigrateFrom); _serializers.insert(mtpc_dialog, _serialize_dialog); _serializers.insert(mtpc_dialogChannel, _serialize_dialogChannel); @@ -7630,6 +7623,7 @@ namespace { _serializers.insert(mtpc_messages_getMessages, _serialize_messages_getMessages); _serializers.insert(mtpc_messages_getHistory, _serialize_messages_getHistory); _serializers.insert(mtpc_messages_search, _serialize_messages_search); + _serializers.insert(mtpc_messages_searchGlobal, _serialize_messages_searchGlobal); _serializers.insert(mtpc_channels_getImportantHistory, _serialize_channels_getImportantHistory); _serializers.insert(mtpc_channels_getMessages, _serialize_channels_getMessages); _serializers.insert(mtpc_messages_getDialogs, _serialize_messages_getDialogs); @@ -7654,7 +7648,6 @@ namespace { _serializers.insert(mtpc_messages_importChatInvite, _serialize_messages_importChatInvite); _serializers.insert(mtpc_messages_startBot, _serialize_messages_startBot); _serializers.insert(mtpc_messages_toggleChatAdmins, _serialize_messages_toggleChatAdmins); - _serializers.insert(mtpc_messages_deactivateChat, _serialize_messages_deactivateChat); _serializers.insert(mtpc_messages_migrateChat, _serialize_messages_migrateChat); _serializers.insert(mtpc_channels_createChannel, _serialize_channels_createChannel); _serializers.insert(mtpc_channels_editTitle, _serialize_channels_editTitle); diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.h b/Telegram/SourceFiles/mtproto/mtpScheme.h index aa8878517..5dd451a57 100644 --- a/Telegram/SourceFiles/mtproto/mtpScheme.h +++ b/Telegram/SourceFiles/mtproto/mtpScheme.h @@ -170,13 +170,11 @@ enum { mtpc_messageActionChatEditTitle = 0xb5a1ce5a, mtpc_messageActionChatEditPhoto = 0x7fcb13a8, mtpc_messageActionChatDeletePhoto = 0x95e3fbef, - mtpc_messageActionChatAddUser = 0x5e3cfc4b, + mtpc_messageActionChatAddUser = 0x488a7337, mtpc_messageActionChatDeleteUser = 0xb2ae9b0c, mtpc_messageActionChatJoinedByLink = 0xf89cf5e8, mtpc_messageActionChannelCreate = 0x95d2ac92, mtpc_messageActionChatMigrateTo = 0x51bdb021, - mtpc_messageActionChatDeactivate = 0x64ad20a8, - mtpc_messageActionChatActivate = 0x40ad8cb2, mtpc_messageActionChannelMigrateFrom = 0xb055eaee, mtpc_dialog = 0xc1dd804a, mtpc_dialogChannel = 0x5b8496b2, @@ -539,12 +537,12 @@ enum { mtpc_messages_getStickerSet = 0x2619a90e, mtpc_messages_installStickerSet = 0x7b30c3a6, mtpc_messages_uninstallStickerSet = 0xf96e55de, - mtpc_messages_startBot = 0x1b3e0ffc, + mtpc_messages_startBot = 0xe6df7378, mtpc_messages_getMessagesViews = 0xc4c8a55d, mtpc_messages_toggleChatAdmins = 0xec8bd9e1, mtpc_messages_editChatAdmin = 0xa9e69f2e, - mtpc_messages_deactivateChat = 0x626f0b41, mtpc_messages_migrateChat = 0x15a3b8e3, + mtpc_messages_searchGlobal = 0x9e3cacb0, mtpc_updates_getState = 0xedd4882a, mtpc_updates_getDifference = 0xa041495, mtpc_updates_getChannelDifference = 0xbb32d7c0, @@ -3884,13 +3882,11 @@ private: friend MTPmessageAction MTP_messageActionChatEditTitle(const MTPstring &_title); friend MTPmessageAction MTP_messageActionChatEditPhoto(const MTPPhoto &_photo); friend MTPmessageAction MTP_messageActionChatDeletePhoto(); - friend MTPmessageAction MTP_messageActionChatAddUser(MTPint _user_id); + friend MTPmessageAction MTP_messageActionChatAddUser(const MTPVector &_users); friend MTPmessageAction MTP_messageActionChatDeleteUser(MTPint _user_id); friend MTPmessageAction MTP_messageActionChatJoinedByLink(MTPint _inviter_id); friend MTPmessageAction MTP_messageActionChannelCreate(const MTPstring &_title); friend MTPmessageAction MTP_messageActionChatMigrateTo(MTPint _channel_id); - friend MTPmessageAction MTP_messageActionChatDeactivate(); - friend MTPmessageAction MTP_messageActionChatActivate(); friend MTPmessageAction MTP_messageActionChannelMigrateFrom(const MTPstring &_title, MTPint _chat_id); mtpTypeId _type; @@ -10099,10 +10095,10 @@ class MTPDmessageActionChatAddUser : public mtpDataImpl &_users) : vusers(_users) { } - MTPint vuser_id; + MTPVector vusers; }; class MTPDmessageActionChatDeleteUser : public mtpDataImpl { @@ -17294,7 +17290,7 @@ public: class MTPmessages_startBot { // RPC method 'messages.startBot' public: MTPInputUser vbot; - MTPint vchat_id; + MTPInputPeer vpeer; MTPlong vrandom_id; MTPstring vstart_param; @@ -17303,24 +17299,24 @@ public: MTPmessages_startBot(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_startBot) { read(from, end, cons); } - MTPmessages_startBot(const MTPInputUser &_bot, MTPint _chat_id, const MTPlong &_random_id, const MTPstring &_start_param) : vbot(_bot), vchat_id(_chat_id), vrandom_id(_random_id), vstart_param(_start_param) { + MTPmessages_startBot(const MTPInputUser &_bot, const MTPInputPeer &_peer, const MTPlong &_random_id, const MTPstring &_start_param) : vbot(_bot), vpeer(_peer), vrandom_id(_random_id), vstart_param(_start_param) { } uint32 innerLength() const { - return vbot.innerLength() + vchat_id.innerLength() + vrandom_id.innerLength() + vstart_param.innerLength(); + return vbot.innerLength() + vpeer.innerLength() + vrandom_id.innerLength() + vstart_param.innerLength(); } mtpTypeId type() const { return mtpc_messages_startBot; } void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_startBot) { vbot.read(from, end); - vchat_id.read(from, end); + vpeer.read(from, end); vrandom_id.read(from, end); vstart_param.read(from, end); } void write(mtpBuffer &to) const { vbot.write(to); - vchat_id.write(to); + vpeer.write(to); vrandom_id.write(to); vstart_param.write(to); } @@ -17335,7 +17331,7 @@ public: } MTPmessages_StartBot(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed(from, end, cons) { } - MTPmessages_StartBot(const MTPInputUser &_bot, MTPint _chat_id, const MTPlong &_random_id, const MTPstring &_start_param) : MTPBoxed(MTPmessages_startBot(_bot, _chat_id, _random_id, _start_param)) { + MTPmessages_StartBot(const MTPInputUser &_bot, const MTPInputPeer &_peer, const MTPlong &_random_id, const MTPstring &_start_param) : MTPBoxed(MTPmessages_startBot(_bot, _peer, _random_id, _start_param)) { } }; @@ -17471,48 +17467,6 @@ public: } }; -class MTPmessages_deactivateChat { // RPC method 'messages.deactivateChat' -public: - MTPint vchat_id; - MTPBool venabled; - - MTPmessages_deactivateChat() { - } - MTPmessages_deactivateChat(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_deactivateChat) { - read(from, end, cons); - } - MTPmessages_deactivateChat(MTPint _chat_id, MTPBool _enabled) : vchat_id(_chat_id), venabled(_enabled) { - } - - uint32 innerLength() const { - return vchat_id.innerLength() + venabled.innerLength(); - } - mtpTypeId type() const { - return mtpc_messages_deactivateChat; - } - void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_deactivateChat) { - vchat_id.read(from, end); - venabled.read(from, end); - } - void write(mtpBuffer &to) const { - vchat_id.write(to); - venabled.write(to); - } - - typedef MTPUpdates ResponseType; -}; -class MTPmessages_DeactivateChat : public MTPBoxed { -public: - MTPmessages_DeactivateChat() { - } - MTPmessages_DeactivateChat(const MTPmessages_deactivateChat &v) : MTPBoxed(v) { - } - MTPmessages_DeactivateChat(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed(from, end, cons) { - } - MTPmessages_DeactivateChat(MTPint _chat_id, MTPBool _enabled) : MTPBoxed(MTPmessages_deactivateChat(_chat_id, _enabled)) { - } -}; - class MTPmessages_migrateChat { // RPC method 'messages.migrateChat' public: MTPint vchat_id; @@ -17552,6 +17506,57 @@ public: } }; +class MTPmessages_searchGlobal { // RPC method 'messages.searchGlobal' +public: + MTPstring vq; + MTPint voffset_date; + MTPInputPeer voffset_peer; + MTPint voffset_id; + MTPint vlimit; + + MTPmessages_searchGlobal() { + } + MTPmessages_searchGlobal(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_searchGlobal) { + read(from, end, cons); + } + MTPmessages_searchGlobal(const MTPstring &_q, MTPint _offset_date, const MTPInputPeer &_offset_peer, MTPint _offset_id, MTPint _limit) : vq(_q), voffset_date(_offset_date), voffset_peer(_offset_peer), voffset_id(_offset_id), vlimit(_limit) { + } + + uint32 innerLength() const { + return vq.innerLength() + voffset_date.innerLength() + voffset_peer.innerLength() + voffset_id.innerLength() + vlimit.innerLength(); + } + mtpTypeId type() const { + return mtpc_messages_searchGlobal; + } + void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_searchGlobal) { + vq.read(from, end); + voffset_date.read(from, end); + voffset_peer.read(from, end); + voffset_id.read(from, end); + vlimit.read(from, end); + } + void write(mtpBuffer &to) const { + vq.write(to); + voffset_date.write(to); + voffset_peer.write(to); + voffset_id.write(to); + vlimit.write(to); + } + + typedef MTPmessages_Messages ResponseType; +}; +class MTPmessages_SearchGlobal : public MTPBoxed { +public: + MTPmessages_SearchGlobal() { + } + MTPmessages_SearchGlobal(const MTPmessages_searchGlobal &v) : MTPBoxed(v) { + } + MTPmessages_SearchGlobal(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed(from, end, cons) { + } + MTPmessages_SearchGlobal(const MTPstring &_q, MTPint _offset_date, const MTPInputPeer &_offset_peer, MTPint _offset_id, MTPint _limit) : MTPBoxed(MTPmessages_searchGlobal(_q, _offset_date, _offset_peer, _offset_id, _limit)) { + } +}; + class MTPupdates_getState { // RPC method 'updates.getState' public: MTPupdates_getState() { @@ -22592,7 +22597,7 @@ inline uint32 MTPmessageAction::innerLength() const { } case mtpc_messageActionChatAddUser: { const MTPDmessageActionChatAddUser &v(c_messageActionChatAddUser()); - return v.vuser_id.innerLength(); + return v.vusers.innerLength(); } case mtpc_messageActionChatDeleteUser: { const MTPDmessageActionChatDeleteUser &v(c_messageActionChatDeleteUser()); @@ -22645,7 +22650,7 @@ inline void MTPmessageAction::read(const mtpPrime *&from, const mtpPrime *end, m case mtpc_messageActionChatAddUser: _type = cons; { if (!data) setData(new MTPDmessageActionChatAddUser()); MTPDmessageActionChatAddUser &v(_messageActionChatAddUser()); - v.vuser_id.read(from, end); + v.vusers.read(from, end); } break; case mtpc_messageActionChatDeleteUser: _type = cons; { if (!data) setData(new MTPDmessageActionChatDeleteUser()); @@ -22667,8 +22672,6 @@ inline void MTPmessageAction::read(const mtpPrime *&from, const mtpPrime *end, m MTPDmessageActionChatMigrateTo &v(_messageActionChatMigrateTo()); v.vchannel_id.read(from, end); } break; - case mtpc_messageActionChatDeactivate: _type = cons; break; - case mtpc_messageActionChatActivate: _type = cons; break; case mtpc_messageActionChannelMigrateFrom: _type = cons; { if (!data) setData(new MTPDmessageActionChannelMigrateFrom()); MTPDmessageActionChannelMigrateFrom &v(_messageActionChannelMigrateFrom()); @@ -22695,7 +22698,7 @@ inline void MTPmessageAction::write(mtpBuffer &to) const { } break; case mtpc_messageActionChatAddUser: { const MTPDmessageActionChatAddUser &v(c_messageActionChatAddUser()); - v.vuser_id.write(to); + v.vusers.write(to); } break; case mtpc_messageActionChatDeleteUser: { const MTPDmessageActionChatDeleteUser &v(c_messageActionChatDeleteUser()); @@ -22732,8 +22735,6 @@ inline MTPmessageAction::MTPmessageAction(mtpTypeId type) : mtpDataOwner(0), _ty case mtpc_messageActionChatJoinedByLink: setData(new MTPDmessageActionChatJoinedByLink()); break; case mtpc_messageActionChannelCreate: setData(new MTPDmessageActionChannelCreate()); break; case mtpc_messageActionChatMigrateTo: setData(new MTPDmessageActionChatMigrateTo()); break; - case mtpc_messageActionChatDeactivate: break; - case mtpc_messageActionChatActivate: break; case mtpc_messageActionChannelMigrateFrom: setData(new MTPDmessageActionChannelMigrateFrom()); break; default: throw mtpErrorBadTypeId(type, "MTPmessageAction"); } @@ -22771,8 +22772,8 @@ inline MTPmessageAction MTP_messageActionChatEditPhoto(const MTPPhoto &_photo) { inline MTPmessageAction MTP_messageActionChatDeletePhoto() { return MTPmessageAction(mtpc_messageActionChatDeletePhoto); } -inline MTPmessageAction MTP_messageActionChatAddUser(MTPint _user_id) { - return MTPmessageAction(new MTPDmessageActionChatAddUser(_user_id)); +inline MTPmessageAction MTP_messageActionChatAddUser(const MTPVector &_users) { + return MTPmessageAction(new MTPDmessageActionChatAddUser(_users)); } inline MTPmessageAction MTP_messageActionChatDeleteUser(MTPint _user_id) { return MTPmessageAction(new MTPDmessageActionChatDeleteUser(_user_id)); @@ -22786,12 +22787,6 @@ inline MTPmessageAction MTP_messageActionChannelCreate(const MTPstring &_title) inline MTPmessageAction MTP_messageActionChatMigrateTo(MTPint _channel_id) { return MTPmessageAction(new MTPDmessageActionChatMigrateTo(_channel_id)); } -inline MTPmessageAction MTP_messageActionChatDeactivate() { - return MTPmessageAction(mtpc_messageActionChatDeactivate); -} -inline MTPmessageAction MTP_messageActionChatActivate() { - return MTPmessageAction(mtpc_messageActionChatActivate); -} inline MTPmessageAction MTP_messageActionChannelMigrateFrom(const MTPstring &_title, MTPint _chat_id) { return MTPmessageAction(new MTPDmessageActionChannelMigrateFrom(_title, _chat_id)); } diff --git a/Telegram/SourceFiles/mtproto/scheme.tl b/Telegram/SourceFiles/mtproto/scheme.tl index 6373795b9..ab179ff87 100644 --- a/Telegram/SourceFiles/mtproto/scheme.tl +++ b/Telegram/SourceFiles/mtproto/scheme.tl @@ -254,13 +254,11 @@ messageActionChatCreate#a6638b9a title:string users:Vector = MessageAction; messageActionChatEditTitle#b5a1ce5a title:string = MessageAction; messageActionChatEditPhoto#7fcb13a8 photo:Photo = MessageAction; messageActionChatDeletePhoto#95e3fbef = MessageAction; -messageActionChatAddUser#5e3cfc4b user_id:int = MessageAction; +messageActionChatAddUser#488a7337 users:Vector = MessageAction; messageActionChatDeleteUser#b2ae9b0c user_id:int = MessageAction; messageActionChatJoinedByLink#f89cf5e8 inviter_id:int = MessageAction; messageActionChannelCreate#95d2ac92 title:string = MessageAction; messageActionChatMigrateTo#51bdb021 channel_id:int = MessageAction; -messageActionChatDeactivate#64ad20a8 = MessageAction; -messageActionChatActivate#40ad8cb2 = MessageAction; messageActionChannelMigrateFrom#b055eaee title:string chat_id:int = MessageAction; dialog#c1dd804a peer:Peer top_message:int read_inbox_max_id:int unread_count:int notify_settings:PeerNotifySettings = Dialog; @@ -735,12 +733,12 @@ messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; messages.installStickerSet#7b30c3a6 stickerset:InputStickerSet disabled:Bool = Bool; messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool; -messages.startBot#1b3e0ffc bot:InputUser chat_id:int random_id:long start_param:string = Updates; +messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates; messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector increment:Bool = Vector; messages.toggleChatAdmins#ec8bd9e1 chat_id:int enabled:Bool = Updates; messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool; -messages.deactivateChat#626f0b41 chat_id:int enabled:Bool = Updates; messages.migrateChat#15a3b8e3 chat_id:int = Updates; +messages.searchGlobal#9e3cacb0 q:string offset_date:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; updates.getState#edd4882a = updates.State; updates.getDifference#a041495 pts:int date:int qts:int = updates.Difference; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 08e17bbb3..090e7f116 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -143,7 +143,8 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, const , _resizeSkip(0) , _peer(App::peer(peer->id)) , _type(type) -, _hist(App::history(peer->id)) +, _migrated(0) +, _history(App::history(peer->id)) , _channel(peerToChannel(peer->id)) , _photosInRow(1) , _photosToAdd(0) @@ -275,11 +276,11 @@ void OverviewInner::fixItemIndex(int32 ¤t, MsgId msgId) const { if (!msgId) { current = -1; } else if (_type == OverviewPhotos || _type == OverviewAudioDocuments) { - int32 l = _hist->overview[_type].size(); - if (current < 0 || current >= l || _hist->overview[_type][current] != msgId) { + int32 l = _history->overview[_type].size(); + if (current < 0 || current >= l || _history->overview[_type][current] != msgId) { current = -1; for (int32 i = 0; i < l; ++i) { - if (_hist->overview[_type][i] == msgId) { + if (_history->overview[_type][i] == msgId) { current = i; break; } @@ -461,11 +462,11 @@ void OverviewInner::moveToNextItem(MsgId &msgId, int32 &index, MsgId upTo, int32 index += delta; if (_type == OverviewPhotos || _type == OverviewAudioDocuments) { - if (index < 0 || index >= _hist->overview[_type].size()) { + if (index < 0 || index >= _history->overview[_type].size()) { msgId = 0; index = -1; } else { - msgId = _hist->overview[_type][index]; + msgId = _history->overview[_type][index]; } } else { while (index >= 0 && index < _items.size() && !_items[index].msgid) { @@ -482,7 +483,7 @@ void OverviewInner::moveToNextItem(MsgId &msgId, int32 &index, MsgId upTo, int32 void OverviewInner::updateMsg(HistoryItem *item) { if (App::main() && item) { - App::main()->msgUpdated(item->history()->peer->id, item); + App::main()->msgUpdated(item); } } @@ -796,7 +797,7 @@ void OverviewInner::onDragExec() { QList urls; bool forwardSelected = false; if (uponSelected) { - forwardSelected = !_selected.isEmpty() && _selected.cbegin().value() == FullItemSel && cWideMode() && !_hist->peer->isChannel(); + forwardSelected = !_selected.isEmpty() && _selected.cbegin().value() == FullItemSel && cWideMode() && !_history->peer->isChannel(); } else if (textlnkDown()) { sel = textlnkDown()->encoded(); if (!sel.isEmpty() && sel.at(0) != '/' && sel.at(0) != '@' && sel.at(0) != '#') { @@ -840,7 +841,7 @@ void OverviewInner::onDragExec() { QDrag *drag = new QDrag(App::wnd()); QMimeData *mimeData = new QMimeData; - if (!_hist->peer->isChannel()) { + if (!_history->peer->isChannel()) { mimeData->setData(qsl("application/x-td-forward-pressed-link"), "1"); } if (lnkDocument) { @@ -874,7 +875,7 @@ void OverviewInner::applyDragSelection() { } if (_dragSelecting) { for (int32 i = _dragSelToIndex; i <= _dragSelFromIndex; ++i) { - MsgId msgid = (_type == OverviewPhotos || _type == OverviewAudioDocuments) ? _hist->overview[_type][i] : _items[i].msgid; + MsgId msgid = (_type == OverviewPhotos || _type == OverviewAudioDocuments) ? _history->overview[_type][i] : _items[i].msgid; if (!msgid) continue; SelectedItems::iterator j = _selected.find(msgid); @@ -893,7 +894,7 @@ void OverviewInner::applyDragSelection() { } } else { for (int32 i = _dragSelToIndex; i <= _dragSelFromIndex; ++i) { - MsgId msgid = (_type == OverviewPhotos || _type == OverviewAudioDocuments) ? _hist->overview[_type][i] : _items[i].msgid; + MsgId msgid = (_type == OverviewPhotos || _type == OverviewAudioDocuments) ? _history->overview[_type][i] : _items[i].msgid; if (!msgid) continue; SelectedItems::iterator j = _selected.find(msgid); @@ -940,7 +941,7 @@ void OverviewInner::clear() { int32 OverviewInner::itemTop(const FullMsgId &msgId) const { if (_type == OverviewAudioDocuments && msgId.channel == _channel) { - int32 index = _hist->overview[_type].indexOf(msgId.msg); + int32 index = _history->overview[_type].indexOf(msgId.msg); if (index >= 0) { return _addToY + int32(index * _audioHeight); } @@ -951,20 +952,20 @@ int32 OverviewInner::itemTop(const FullMsgId &msgId) const { void OverviewInner::preloadMore() { if (_inSearch) { if (!_searchRequest && !_searchFull) { - int32 flags = (_hist->peer->isChannel() && !_hist->peer->isMegagroup()) ? MTPmessages_Search::flag_important_only : 0; - _searchRequest = MTP::send(MTPmessages_Search(MTP_int(flags), _hist->peer->input, MTP_string(_searchQuery), MTP_inputMessagesFilterUrl(), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(_lastSearchId), MTP_int(SearchPerPage)), rpcDone(&OverviewInner::searchReceived, !_lastSearchId), rpcFail(&OverviewInner::searchFailed)); + int32 flags = (_history->peer->isChannel() && !_history->peer->isMegagroup()) ? MTPmessages_Search::flag_important_only : 0; + _searchRequest = MTP::send(MTPmessages_Search(MTP_int(flags), _history->peer->input, MTP_string(_searchQuery), MTP_inputMessagesFilterUrl(), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(_lastSearchId), MTP_int(SearchPerPage)), rpcDone(&OverviewInner::searchReceived, !_lastSearchId), rpcFail(&OverviewInner::searchFailed)); if (!_lastSearchId) { _searchQueries.insert(_searchRequest, _searchQuery); } } } else if (App::main()) { - App::main()->loadMediaBack(_hist->peer, _type, _type != OverviewLinks); + App::main()->loadMediaBack(_history->peer, _type, _type != OverviewLinks); } } bool OverviewInner::preloadLocal() { if (_type != OverviewLinks) return false; - if (_itemsToBeLoaded >= _hist->overview[_type].size()) return false; + if (_itemsToBeLoaded >= _history->overview[_type].size()) return false; _itemsToBeLoaded += LinksOverviewPerPage; mediaOverviewUpdated(); return true; @@ -999,7 +1000,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); p.setClipRect(r); - if (_hist->overview[_type].isEmpty()) { + if (_history->overview[_type].isEmpty()) { QPoint dogPos((_width - st::msgDogImg.pxWidth()) / 2, ((height() - st::msgDogImg.pxHeight()) * 4) / 9); p.drawPixmap(dogPos, *cChatDogImage()); return; @@ -1020,7 +1021,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { bool hasSel = !_selected.isEmpty(); if (_type == OverviewPhotos) { - History::MediaOverview &overview(_hist->overview[_type]); + History::MediaOverview &overview(_history->overview[_type]); int32 count = overview.size(); int32 rowFrom = floorclamp(r.y() - _addToY - st::overviewPhotoSkip, _vsize + st::overviewPhotoSkip, 0, count); int32 rowTo = ceilclamp(r.y() + r.height() - _addToY - st::overviewPhotoSkip, _vsize + st::overviewPhotoSkip, 0, count); @@ -1103,7 +1104,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { } } } else if (_type == OverviewAudioDocuments) { - History::MediaOverview &overview(_hist->overview[_type]); + History::MediaOverview &overview(_history->overview[_type]); int32 count = overview.size(); int32 from = floorclamp(r.y() - _addToY, _audioHeight, 0, count); int32 to = ceilclamp(r.y() + r.height() - _addToY, _audioHeight, 0, count); @@ -1308,7 +1309,7 @@ void OverviewInner::onUpdateSelected() { if (row < 0) row = 0; bool upon = true; - int32 i = row * _photosInRow + inRow - _photosToAdd, count = _hist->overview[_type].size(); + int32 i = row * _photosInRow + inRow - _photosToAdd, count = _history->overview[_type].size(); if (i < 0) { i = 0; upon = false; @@ -1318,7 +1319,7 @@ void OverviewInner::onUpdateSelected() { upon = false; } if (i >= 0) { - MsgId msgid = _hist->overview[_type][i]; + MsgId msgid = _history->overview[_type][i]; HistoryItem *histItem = App::histItemById(_channel, msgid); if (histItem) { item = histItem; @@ -1334,7 +1335,7 @@ void OverviewInner::onUpdateSelected() { } } } else if (_type == OverviewAudioDocuments) { - int32 i = int32((m.y() - _addToY) / _audioHeight), count = _hist->overview[_type].size(); + int32 i = int32((m.y() - _addToY) / _audioHeight), count = _history->overview[_type].size(); bool upon = true; if (m.y() < _addToY) { @@ -1346,7 +1347,7 @@ void OverviewInner::onUpdateSelected() { upon = false; } if (i >= 0) { - MsgId msgid = _hist->overview[_type][i]; + MsgId msgid = _history->overview[_type][i]; HistoryItem *histItem = App::histItemById(_channel, msgid); if (histItem) { item = histItem; @@ -2033,8 +2034,8 @@ bool OverviewInner::onSearchMessages(bool searchCache) { } else if (_searchQuery != q) { _searchQuery = q; _searchFull = false; - int32 flags = (_hist->peer->isChannel() && !_hist->peer->isMegagroup()) ? MTPmessages_Search::flag_important_only : 0; - _searchRequest = MTP::send(MTPmessages_Search(MTP_int(flags), _hist->peer->input, MTP_string(_searchQuery), MTP_inputMessagesFilterUrl(), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(SearchPerPage)), rpcDone(&OverviewInner::searchReceived, true), rpcFail(&OverviewInner::searchFailed)); + int32 flags = (_history->peer->isChannel() && !_history->peer->isMegagroup()) ? MTPmessages_Search::flag_important_only : 0; + _searchRequest = MTP::send(MTPmessages_Search(MTP_int(flags), _history->peer->input, MTP_string(_searchQuery), MTP_inputMessagesFilterUrl(), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(SearchPerPage)), rpcDone(&OverviewInner::searchReceived, true), rpcFail(&OverviewInner::searchFailed)); _searchQueries.insert(_searchRequest, _searchQuery); } return false; @@ -2132,7 +2133,11 @@ void OverviewInner::fillSelectedItems(SelectedItemSet &sel, bool forDelete) { for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { HistoryItem *item = App::histItemById(_channel, i.key()); if (item && item->toHistoryMessage() && item->id > 0) { - sel.insert(item->id, item); + if (item->history() == _migrated) { + sel.insert(item->id - ServerMaxMsgId, item); + } else { + sel.insert(item->id, item); + } } } } @@ -2166,7 +2171,7 @@ void OverviewInner::onTouchScrollTimer() { void OverviewInner::mediaOverviewUpdated(bool fromResize) { int32 oldHeight = _height; if (_type == OverviewLinks) { - History::MediaOverview &o(_inSearch ? _searchResults : _hist->overview[_type]); + History::MediaOverview &o(_inSearch ? _searchResults : _history->overview[_type]); int32 l = o.size(), tocheck = qMin(l, _itemsToBeLoaded); _items.reserve(2 * l); // day items @@ -2244,7 +2249,7 @@ void OverviewInner::mediaOverviewUpdated(bool fromResize) { dragActionUpdate(QCursor::pos()); update(); } else if (_type != OverviewPhotos && _type != OverviewAudioDocuments) { - History::MediaOverview &o(_hist->overview[_type]); + History::MediaOverview &o(_history->overview[_type]); int32 l = o.size(); _items.reserve(2 * l); // day items @@ -2455,11 +2460,11 @@ void OverviewInner::itemResized(HistoryItem *item, bool scrollToIt) { } void OverviewInner::msgUpdated(const HistoryItem *msg) { - if (!msg || _hist != msg->history()) return; + if (!msg || _history != msg->history()) return; MsgId msgid = msg->id; - if (_hist->overviewIds[_type].constFind(msgid) != _hist->overviewIds[_type].cend()) { + if (_history->overviewIds[_type].constFind(msgid) != _history->overviewIds[_type].cend()) { if (_type == OverviewPhotos) { - int32 index = _hist->overview[_type].indexOf(msgid); + int32 index = _history->overview[_type].indexOf(msgid); if (index >= 0) { float64 w = (float64(width() - st::overviewPhotoSkip) / _photosInRow); int32 vsize = (_vsize + st::overviewPhotoSkip); @@ -2467,7 +2472,7 @@ void OverviewInner::msgUpdated(const HistoryItem *msg) { update(int32(col * w), _addToY + int32(row * vsize), qCeil(w), vsize); } } else if (_type == OverviewAudioDocuments) { - int32 index = _hist->overview[_type].indexOf(msgid); + int32 index = _history->overview[_type].indexOf(msgid); if (index >= 0) { update(_audioLeft, _addToY + int32(index * _audioHeight), _audioWidth, _audioHeight); } @@ -2494,7 +2499,7 @@ void OverviewInner::showAll(bool recountHeights) { if (_type == OverviewPhotos) { _photosInRow = int32(width() - st::overviewPhotoSkip) / int32(st::overviewPhotoMinSize + st::overviewPhotoSkip); _vsize = (int32(width() - st::overviewPhotoSkip) / _photosInRow) - st::overviewPhotoSkip; - int32 count = _hist->overview[_type].size(), fullCount = _hist->overviewCount[_type]; + int32 count = _history->overview[_type].size(), fullCount = _history->overviewCount[_type]; if (fullCount > 0) { int32 cnt = count - (fullCount % _photosInRow); if (cnt < 0) cnt += _photosInRow; @@ -2506,7 +2511,7 @@ void OverviewInner::showAll(bool recountHeights) { newHeight = _height = (_vsize + st::overviewPhotoSkip) * rows + st::overviewPhotoSkip; _addToY = (_height < _minHeight) ? (_minHeight - _height) : 0; } else if (_type == OverviewAudioDocuments) { - int32 count = _hist->overview[_type].size(), fullCount = _hist->overviewCount[_type]; + int32 count = _history->overview[_type].size(), fullCount = _history->overviewCount[_type]; newHeight = _height = count * _audioHeight + 2 * st::playlistPadding; _addToY = st::playlistPadding; } else if (_type == OverviewLinks) { @@ -2869,8 +2874,8 @@ void OverviewWidget::changingMsgId(HistoryItem *row, MsgId newId) { } } -void OverviewWidget::msgUpdated(PeerId p, const HistoryItem *msg) { - if (peer()->id == p) { +void OverviewWidget::msgUpdated(const HistoryItem *msg) { + if (peer() == msg->history()->peer) { _inner.msgUpdated(msg); } } @@ -2983,10 +2988,10 @@ void OverviewWidget::onDeleteSelectedSure() { _inner.fillSelectedItems(sel); if (sel.isEmpty()) return; - QVector ids; + QMap > ids; for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) { if (i.value()->id > 0) { - ids.push_back(MTP_int(i.value()->id)); + ids[i.value()->history()->peer].push_back(MTP_int(i.value()->id)); } } @@ -2999,8 +3004,8 @@ void OverviewWidget::onDeleteSelectedSure() { } App::wnd()->hideLayer(); - if (!ids.isEmpty()) { - App::main()->deleteMessages(peer(), ids); + for (QMap >::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) { + App::main()->deleteMessages(i.key(), i.value()); } } @@ -3018,7 +3023,7 @@ void OverviewWidget::onDeleteContextSure() { App::main()->checkPeerHistory(h->peer); } - if (App::main() && App::main()->peer() == peer()) { + if (App::main() && (App::main()->peer() == h->peer || (App::main()->peer() && App::main()->peer() == h->peer->migrateTo()))) { App::main()->itemResized(0); } App::wnd()->hideLayer(); diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index 954dd6780..4cf48b5f1 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -135,7 +135,7 @@ private: PeerData *_peer; MediaOverviewType _type; - History *_hist; + History *_migrated, *_history; ChannelId _channel; // photos @@ -300,7 +300,7 @@ public: void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type); void changingMsgId(HistoryItem *row, MsgId newId); - void msgUpdated(PeerId peer, const HistoryItem *msg); + void msgUpdated(const HistoryItem *msg); void itemRemoved(HistoryItem *item); void itemResized(HistoryItem *row, bool scrollToIt); diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index f3ef9a6e8..65076b94c 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -806,10 +806,10 @@ void ProfileInner::paintEvent(QPaintEvent *e) { p.setPen(st::profileOfflineColor->p); p.drawText(_left + (_width - w) / 2, top + st::btnShareContact.textTop + st::btnShareContact.font->ascent, lang(lng_profile_chat_unaccessible)); } - if ((!_peerChat || _peerChat->canEdit()) && (!_peerChannel || _amCreator)) { + if ((!_peerChat || _peerChat->canEdit()) && (!_peerChannel || _amCreator || (_peerChannel->amEditor() && _peerChannel->isMegagroup()))) { top += _shareContact.height(); } - + // about if (!_about.isEmpty()) { p.setFont(st::profileHeaderFont->f); @@ -1215,6 +1215,7 @@ void ProfileInner::migrateDone(const MTPUpdates &updates) { if (v->at(i).type() == mtpc_channel) { peer = App::channel(v->at(i).c_channel().vid.v); App::main()->showPeerHistory(peer->id, ShowAtUnreadMsgId); + QTimer::singleShot(ReloadChannelMembersTimeout, App::api(), SLOT(delayedRequestParticipantsCount())); } } } @@ -1263,7 +1264,7 @@ void ProfileInner::resizeEvent(QResizeEvent *e) { _shareContact.setGeometry(_left + _width - btnWidth, top, btnWidth, _shareContact.height()); _inviteToGroup.setGeometry(_left + _width - btnWidth, top, btnWidth, _inviteToGroup.height()); - if ((!_peerChat || _peerChat->canEdit()) && (!_peerChannel || _amCreator)) { + if ((!_peerChat || _peerChat->canEdit()) && (!_peerChannel || _amCreator || (_peerChannel->amEditor() && _peerChannel->isMegagroup()))) { top += _shareContact.height(); } diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 5c4490567..1d27c9def 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -411,6 +411,24 @@ void ChannelData::fullUpdated() { _lastFullUpdate = getms(true); } +void ChannelData::flagsUpdated() { + if (isMegagroup()) { + if (!mgInfo) { + mgInfo = new MegagroupInfo(); + } + if (History *h = App::historyLoaded(id)) { + if (h->asChannelHistory()->onlyImportant()) { + MsgId fixInScrollMsgId = 0; + int32 fixInScrollMsgTop = 0; + h->asChannelHistory()->getSwitchReadyFor(SwitchAtTopMsgId, fixInScrollMsgId, fixInScrollMsgTop); + } + } + } else if (mgInfo) { + delete mgInfo; + mgInfo = 0; + } +} + ChannelData::~ChannelData() { delete mgInfo; } diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 97d639641..69f838bf2 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -226,6 +226,9 @@ public: ChannelData *asChannel(); const ChannelData *asChannel() const; + ChatData *migrateFrom() const; + ChannelData *migrateTo() const; + void updateName(const QString &newName, const QString &newNameOrPhone, const QString &newUsername); void fillNames(); @@ -383,7 +386,7 @@ public: class ChatData : public PeerData { public: - 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) { + ChatData(const PeerId &id) : PeerData(id), inputChat(MTP_int(bareId())), migrateToPtr(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() { @@ -399,7 +402,7 @@ public: MTPint inputChat; - ChannelData *migrateTo; + ChannelData *migrateToPtr; int32 count; int32 date; @@ -516,7 +519,7 @@ private: }; struct MegagroupInfo { - MegagroupInfo() : botStatus(-1), migrateFrom(0) { + MegagroupInfo() : botStatus(-1), migrateFromPtr(0) { } typedef QList LastParticipants; LastParticipants lastParticipants; @@ -527,7 +530,7 @@ struct MegagroupInfo { typedef QMap Bots; Bots bots; int32 botStatus; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other - ChatData *migrateFrom; + ChatData *migrateFromPtr; }; class ChannelData : public PeerData { @@ -553,6 +556,7 @@ public: int32 version; int32 flags, flagsFull; MegagroupInfo *mgInfo; + void flagsUpdated(); bool isMegagroup() const { return flags & MTPDchannel::flag_megagroup; } @@ -659,6 +663,12 @@ inline ChannelData *PeerData::asChannel() { inline const ChannelData *PeerData::asChannel() const { return isChannel() ? static_cast(this) : 0; } +inline ChatData *PeerData::migrateFrom() const { + return (isMegagroup() && asChannel()->amIn()) ? asChannel()->mgInfo->migrateFromPtr : 0; +} +inline ChannelData *PeerData::migrateTo() const { + return (isChat() && asChat()->migrateToPtr && asChat()->migrateToPtr->amIn()) ? asChat()->migrateToPtr : 0; +} inline const Text &PeerData::dialogName() const { return (isUser() && !asUser()->phoneText.isEmpty()) ? asUser()->phoneText : nameText; } @@ -1193,7 +1203,7 @@ struct MessageCursor { position = c.position(); anchor = c.anchor(); QScrollBar *s = edit.verticalScrollBar(); - scroll = s ? s->value() : QFIXED_MAX; + scroll = (s && (s->value() != s->maximum())) ? s->value() : QFIXED_MAX; } void applyTo(QTextEdit &edit, bool *lock = 0) { if (lock) *lock = true;