From 56a63a5b105224c9717a376c4f32021f5ce7ca8e Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 19 Sep 2015 12:13:21 +0300 Subject: [PATCH] comments show-hide in channels --- Telegram/Resources/lang.strings | 6 +- Telegram/Resources/style.txt | 18 + Telegram/SourceFiles/apiwrap.cpp | 12 +- Telegram/SourceFiles/app.cpp | 38 +- Telegram/SourceFiles/app.h | 8 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 10 +- Telegram/SourceFiles/boxes/photosendbox.cpp | 2 +- Telegram/SourceFiles/config.h | 2 +- Telegram/SourceFiles/dialogswidget.cpp | 39 +- Telegram/SourceFiles/gui/flatbutton.cpp | 4 + Telegram/SourceFiles/gui/flatbutton.h | 1 + Telegram/SourceFiles/history.cpp | 1131 ++++++++++++++----- Telegram/SourceFiles/history.h | 276 ++++- Telegram/SourceFiles/historywidget.cpp | 441 +++++--- Telegram/SourceFiles/historywidget.h | 29 +- Telegram/SourceFiles/mainwidget.cpp | 168 ++- Telegram/SourceFiles/mainwidget.h | 4 +- Telegram/SourceFiles/mediaview.cpp | 42 +- Telegram/SourceFiles/mediaview.h | 1 - Telegram/SourceFiles/mtproto/mtpScheme.cpp | 31 +- Telegram/SourceFiles/mtproto/mtpScheme.h | 123 +- Telegram/SourceFiles/mtproto/scheme.tl | 6 +- Telegram/SourceFiles/overviewwidget.cpp | 83 +- Telegram/SourceFiles/playerwidget.cpp | 14 +- Telegram/SourceFiles/profilewidget.cpp | 2 +- Telegram/SourceFiles/structs.cpp | 6 + Telegram/SourceFiles/structs.h | 1 + Telegram/SourceFiles/types.h | 2 + 28 files changed, 1762 insertions(+), 738 deletions(-) diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index dfe5f260a..a2540285b 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -456,8 +456,9 @@ Copyright (c) 2014 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_comments_disabled" = "Comments were disabled"; -"lng_action_comments_enabled" = "Comments were enabled"; + +"lng_channel_comments_count" = "{count:_not_used_|# comment|# comments}"; +"lng_channel_hide_comments" = "Hide comments"; "lng_group_invite_bad_link" = "This invite link is broken\nor has expired."; "lng_group_invite_want_join" = "Do you want to join the group «{title}»?"; @@ -654,6 +655,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_try_other_contact" = "Try other"; "lng_contacts_done" = "Close"; "lng_create_group_link" = "Link"; +"lng_create_group_invite_link" = "Invite link"; "lng_create_group_photo" = "Set Photo"; "lng_create_group_description" = "Description (optional)"; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 485db1ade..b7bfd8d1a 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -909,6 +909,24 @@ msgDateImgCheckSpace: 4px; msgDogImg: sprite(213px, 93px, 126px, 126px); historyPadding: 10px; +collapseButton: flatButton(btnDefFlat) { + font: msgServiceFont; + overFont: msgServiceFont; + width: -24px; + bgColor: transparent; + downBgColor: transparent; + overBgColor: transparent; + color: white; + overColor: white; + downColor: white; + textTop: 3px; + overTextTop: 3px; + downTextTop: 3px; + height: 24px; +} +collapseHideDuration: 200; +collapseShowDuration: 200; + defaultTextStyle: textStyle { lnkFlags: font(fsize); lnkOverFlags: font(fsize underline); diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index cbb84fa34..f6d5e124d 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -168,7 +168,10 @@ void ApiWrap::gotReplyTo(ChannelData *channel, const MTPmessages_Messages &msgs, if (channel) { channel->ptsReceived(d.vpts.v); } else { - LOG(("App Error: received messages.channelMessages in ApiWrap::gotReplyTo when no channel was passed!")); + LOG(("App Error: received messages.channelMessages when no channel was passed! (ApiWrap::gotReplyTo)")); + } + if (d.has_collapsed()) { // should not be returned + LOG(("API Error: channels.getMessages and messages.getMessages should not return collapsed groups! (ApiWrap::gotReplyTo)")); } App::feedUsers(d.vusers); @@ -619,7 +622,10 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs if (channel) { channel->ptsReceived(d.vpts.v); } else { - LOG(("App Error: received messages.channelMessages in ApiWrap::gotWebPages when no channel was passed!")); + LOG(("API Error: received messages.channelMessages when no channel was passed! (ApiWrap::gotWebPages)")); + } + if (d.has_collapsed()) { // should not be returned + LOG(("API Error: channels.getMessages and messages.getMessages should not return collapsed groups! (ApiWrap::gotWebPages)")); } App::feedUsers(d.vusers); @@ -641,7 +647,7 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs MainWidget *m = App::main(); for (QMap::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) { - HistoryItem *item = App::histories().addToBack(v->at(i.value()), -1); + HistoryItem *item = App::histories().addNewMessage(v->at(i.value()), -1); if (item) { item->initDimensions(); if (m) m->itemResized(item); diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index b983577ff..b0826de09 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -784,7 +784,7 @@ namespace App { } } for (QMap::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) { - histories().addToBack(v.at(i.value()), msgsState); + histories().addNewMessage(v.at(i.value()), msgsState); } } @@ -871,19 +871,30 @@ namespace App { MsgsData *data = fetchMsgsData(channelId, false); if (!data) return; + ChannelHistory *channelHistory = (channelId == NoChannel) ? 0 : App::historyLoaded(peerFromChannel(channelId))->asChannelHistory(); + + QMap historiesToCheck; for (QVector::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) { MsgsData::const_iterator j = data->constFind(i->v); if (j != data->cend()) { History *h = (*j)->history(); - (*j)->destroy(); - if (App::main() && h->peer == App::main()->peer()) { + if (App::main() && h->peer == App::main()->peer() && !(*j)->detached()) { resized = true; } + (*j)->destroy(); + if (!h->lastMsg) historiesToCheck.insert(h, true); + } else if (channelHistory) { + channelHistory->messageWithIdDeleted(i->v); } } if (resized) { App::main()->itemResized(0); } + if (main()) { + for (QMap::const_iterator i = historiesToCheck.cbegin(), e = historiesToCheck.cend(); i != e; ++i) { + main()->checkPeerHistory(i.key()->peer); + } + } } void feedUserLinks(const MTPVector &links, bool emitPeerUpdated) { @@ -1630,26 +1641,15 @@ namespace App { } History *history(const PeerId &peer) { - Histories::const_iterator i = ::histories.constFind(peer); - if (i == ::histories.cend()) { - i = App::histories().insert(peer, new History(peer)); - } - return i.value(); + return ::histories.findOrInsert(peer, 0, 0); } History *historyFromDialog(const PeerId &peer, int32 unreadCnt, int32 maxInboxRead) { - Histories::const_iterator i = ::histories.constFind(peer); - if (i == ::histories.cend()) { - i = App::histories().insert(peer, new History(peer)); - i.value()->setUnreadCount(unreadCnt, false); - i.value()->inboxReadBefore = maxInboxRead + 1; - } - return i.value(); + return ::histories.findOrInsert(peer, unreadCnt, maxInboxRead); } History *historyLoaded(const PeerId &peer) { - Histories::const_iterator i = ::histories.constFind(peer); - return (i == ::histories.cend()) ? 0 : i.value(); + return ::histories.find(peer); } HistoryItem *histItemById(ChannelId channelId, MsgId itemId) { @@ -2406,7 +2406,7 @@ namespace App { return ::corners[index]; } - void roundRect(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh) { + void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh) { QPixmap **c = ::corners[index]; int32 cw = c[0]->width() / cIntRetinaFactor(), ch = c[0]->height() / cIntRetinaFactor(); if (w < 2 * cw || h < 2 * ch) return; @@ -2424,7 +2424,7 @@ namespace App { p.drawPixmap(QPoint(x + w - cw, y + h - ch), *c[3]); } - void roundShadow(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &sh, RoundCorners index) { + void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &sh, RoundCorners index) { QPixmap **c = App::corners(index); int32 cw = c[0]->width() / cIntRetinaFactor(), ch = c[0]->height() / cIntRetinaFactor(); p.fillRect(x + cw, y + h, w - 2 * cw, st::msgShadow, sh->b); diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index b2d30dadb..ab3a01c33 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -261,12 +261,12 @@ namespace App { QImage **cornersMask(); QPixmap **corners(RoundCorners index); - void roundRect(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh = 0); - inline void roundRect(QPainter &p, const QRect &rect, const style::color &bg, RoundCorners index, const style::color *sh = 0) { + 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) { return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, sh); } - void roundShadow(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &sh, RoundCorners index); - inline void roundShadow(QPainter &p, const QRect &rect, const style::color &sh, RoundCorners index) { + void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &sh, RoundCorners index); + inline void roundShadow(Painter &p, const QRect &rect, const style::color &sh, RoundCorners index) { return roundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), sh, index); } diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 5ab7bdcca..bc1e81ccb 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -207,7 +207,7 @@ ContactsInner::ContactData *ContactsInner::contactData(DialogRow *row) { data->online = lng_chat_status_members(lt_count, chat->count); } } else if (peer->isChannel()) { - data->online = lang(lng_chat_status_unaccessible); // CHANNELS_UX + data->online = lang(lng_channel_status); } } else { data = i.value(); @@ -1623,7 +1623,7 @@ void GroupInfoBox::onNext() { if (_creating == CreatingGroupGroup) { App::wnd()->replaceLayer(new ContactsBox(name, _photoBig)); } else { - _creationRequestId = MTP::send(MTPchannels_CreateChannel(MTP_int(MTPmessages_CreateChannel_flag_broadcast), MTP_string(name), MTP_string(_description.getLastText().trimmed()), MTP_vector(0)), rpcDone(&GroupInfoBox::creationDone), rpcFail(&GroupInfoBox::creationFail)); + _creationRequestId = MTP::send(MTPchannels_CreateChannel(MTP_int(0), MTP_string(name), MTP_string(_description.getLastText().trimmed()), MTP_vector(0)), rpcDone(&GroupInfoBox::creationDone), rpcFail(&GroupInfoBox::creationFail)); } } @@ -1804,7 +1804,7 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) { p.setPen(st::black); p.setFont(st::newGroupLinkFont); - p.drawTextLeft(st::newGroupPadding.left(), _link.y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop, width(), lang(lng_create_group_link)); + p.drawTextLeft(st::newGroupPadding.left(), _link.y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop, width(), lang(_link.isHidden() ? lng_create_group_invite_link : lng_create_group_link)); if (_link.isHidden()) { QTextOption option(style::al_left); @@ -1899,8 +1899,8 @@ void SetupChannelBox::closePressed() { void SetupChannelBox::onSave() { if (!_public.checked()) { - if (_comments.checked()) { - MTP::send(MTPchannels_ToggleComments(_channel->inputChannel, MTP_bool(true))); + if (!_comments.checked()) { + MTP::send(MTPchannels_ToggleComments(_channel->inputChannel, MTP_bool(false))); } onClose(); } diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index 6cff7cff7..5ae743d90 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -124,7 +124,7 @@ void PhotoSendBox::keyPressEvent(QKeyEvent *e) { } void PhotoSendBox::paintEvent(QPaintEvent *e) { - QPainter p(this); + Painter p(this); if (paint(p)) return; // paint shadow diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 1199dc9dd..e4ea84310 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -123,7 +123,7 @@ enum { MaxUsernameLength = 32, UsernameCheckTimeout = 200, - MaxChannelDescription = 255, + MaxChannelDescription = 120, MaxMessageSize = 4096, MaxHttpRedirects = 5, // when getting external data/images diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index d88cafbef..0f90816b4 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -779,8 +779,10 @@ void DialogsListWidget::dialogsReceived(const QVector &added) { const MTPDdialogChannel &d(i->c_dialogChannel()); History *history = App::historyFromDialog(peerFromMTP(d.vpeer), d.vunread_important_count.v, d.vread_inbox_max_id.v); if (history->peer->isChannel()) { + history->asChannelHistory()->unreadCountAll = d.vunread_count.v; history->peer->asChannel()->ptsReceived(d.vpts.v); } + if (d.vtop_message.v > d.vtop_important_message.v) history->setNotLoadedAtBottom(); App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, history); } break; } @@ -821,7 +823,7 @@ void DialogsListWidget::searchReceived(const QVector &messages, bool clearSearchResults(false); } for (QVector::const_iterator i = messages.cbegin(), e = messages.cend(); i != e; ++i) { - HistoryItem *item = App::histories().addToBack(*i, -1); + HistoryItem *item = App::histories().addNewMessage(*i, -1); searchResults.push_back(new FakeDialogRow(item)); _lastSearchId = item->id; } @@ -1602,26 +1604,28 @@ void DialogsWidget::unreadCountsReceived(const QVector &dialogs) { switch (i->type()) { case mtpc_dialog: { const MTPDdialog &d(i->c_dialog()); - Histories::iterator j = App::histories().find(peerFromMTP(d.vpeer)); - if (j != App::histories().end()) { - App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, j.value()); - if (d.vunread_count.v >= j.value()->unreadCount) { - j.value()->setUnreadCount(d.vunread_count.v, false); - j.value()->inboxReadBefore = d.vread_inbox_max_id.v + 1; + if (History *h = App::historyLoaded(peerFromMTP(d.vpeer))) { + App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, h); + if (d.vunread_count.v >= h->unreadCount) { + h->setUnreadCount(d.vunread_count.v, false); + h->inboxReadBefore = d.vread_inbox_max_id.v + 1; } } } break; case mtpc_dialogChannel: { const MTPDdialogChannel &d(i->c_dialogChannel()); - Histories::iterator j = App::histories().find(peerFromMTP(d.vpeer)); - if (j != App::histories().end()) { - if (j.value()->peer->isChannel()) { - j.value()->peer->asChannel()->ptsReceived(d.vpts.v); + if (History *h = App::historyLoaded(peerFromMTP(d.vpeer))) { + if (h->peer->isChannel()) { + h->peer->asChannel()->ptsReceived(d.vpts.v); + if (d.vunread_count.v >= h->asChannelHistory()->unreadCountAll) { + h->asChannelHistory()->unreadCountAll = d.vunread_count.v; + h->inboxReadBefore = d.vread_inbox_max_id.v + 1; + } } - App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, j.value()); - if (d.vunread_important_count.v >= j.value()->unreadCount) { - j.value()->setUnreadCount(d.vunread_important_count.v, false); - j.value()->inboxReadBefore = d.vread_inbox_max_id.v + 1; + App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, h); + if (d.vunread_important_count.v >= h->unreadCount) { + h->setUnreadCount(d.vunread_important_count.v, false); + h->inboxReadBefore = d.vread_inbox_max_id.v + 1; } } } break; @@ -1863,7 +1867,10 @@ void DialogsWidget::searchReceived(bool fromStart, const MTPmessages_Messages &r if (_searchInPeer && _searchInPeer->isChannel()) { _searchInPeer->asChannel()->ptsReceived(d.vpts.v); } else { - LOG(("App Error: received messages.channelMessages in DialogsWidget::searchReceived when no channel was passed!")); + LOG(("API Error: received messages.channelMessages when no channel was passed! (DialogsWidget::searchReceived)")); + } + if (d.has_collapsed()) { // should not be returned + LOG(("API Error: channels.getMessages and messages.getMessages should not return collapsed groups! (DialogsWidget::searchReceived)")); } App::feedUsers(d.vusers); diff --git a/Telegram/SourceFiles/gui/flatbutton.cpp b/Telegram/SourceFiles/gui/flatbutton.cpp index eea7e4adf..ac36eb12c 100644 --- a/Telegram/SourceFiles/gui/flatbutton.cpp +++ b/Telegram/SourceFiles/gui/flatbutton.cpp @@ -37,6 +37,10 @@ void FlatButton::setOpacity(float64 o) { update(); } +float64 FlatButton::opacity() const { + return _opacity; +} + void FlatButton::setText(const QString &text) { _text = text; update(); diff --git a/Telegram/SourceFiles/gui/flatbutton.h b/Telegram/SourceFiles/gui/flatbutton.h index 2bc18fd6f..98779f4d4 100644 --- a/Telegram/SourceFiles/gui/flatbutton.h +++ b/Telegram/SourceFiles/gui/flatbutton.h @@ -34,6 +34,7 @@ public: bool animStep(float64 ms); void paintEvent(QPaintEvent *e); void setOpacity(float64 o); + float64 opacity() const; void setText(const QString &text); void setWidth(int32 w); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index d8b937726..e87e62260 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -315,7 +315,7 @@ History::History(const PeerId &peerId) : width(0), height(0) , lastMsg(0) , draftToId(0) , lastWidth(0) -, lastScrollTop(History::ScrollMax) +, lastScrollTop(ScrollMax) , lastShowAtMsgId(ShowAtUnreadMsgId) , mute(isNotifyMuted(peer->notify)) , lastKeyboardInited(false) @@ -332,7 +332,7 @@ History::History(const PeerId &peerId) : width(0), height(0) outboxReadBefore = INT_MAX; } for (int32 i = 0; i < OverviewCount; ++i) { - _overviewCount[i] = -1; // not loaded yet + overviewCount[i] = -1; // not loaded yet } } @@ -398,19 +398,19 @@ bool History::updateTyping(uint64 ms, uint32 dots, bool force) { } void History::eraseFromOverview(MediaOverviewType type, MsgId msgId) { - if (_overviewIds[type].isEmpty()) return; + if (overviewIds[type].isEmpty()) return; - History::MediaOverviewIds::iterator i = _overviewIds[type].find(msgId); - if (i == _overviewIds[type].cend()) return; + History::MediaOverviewIds::iterator i = overviewIds[type].find(msgId); + if (i == overviewIds[type].cend()) return; - _overviewIds[type].erase(i); - for (History::MediaOverview::iterator i = _overview[type].begin(), e = _overview[type].end(); i != e; ++i) { + overviewIds[type].erase(i); + for (History::MediaOverview::iterator i = overview[type].begin(), e = overview[type].end(); i != e; ++i) { if ((*i) == msgId) { - _overview[type].erase(i); - if (_overviewCount[type] > 0) { - --_overviewCount[type]; - if (!_overviewCount[type]) { - _overviewCount[type] = -1; + overview[type].erase(i); + if (overviewCount[type] > 0) { + --overviewCount[type]; + if (!overviewCount[type]) { + overviewCount[type] = -1; } } break; @@ -419,6 +419,311 @@ void History::eraseFromOverview(MediaOverviewType type, MsgId msgId) { if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer, type); } +ChannelHistory::ChannelHistory(const PeerId &peer) : History(peer), +unreadCountAll(0), +_onlyImportant(true), +_otherOldLoaded(false), _otherNewLoaded(false), +_otherMsgCount(0), +_collapse(0) { +} + +bool ChannelHistory::isSwitchReadyFor(MsgId switchId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop) { + if (switchId == SwitchAtTopMsgId) { + if (_onlyImportant) return true; + + int32 bottomUnderScrollTop = 0; + HistoryItem *atTopItem = App::main()->atTopImportantMsg(bottomUnderScrollTop); + if (atTopItem) { + fixInScrollMsgId = atTopItem->id; + fixInScrollMsgTop = atTopItem->y + atTopItem->block()->y + atTopItem->height() - bottomUnderScrollTop - height; + if (_otherList.indexOf(atTopItem) >= 0) { + switchMode(); + return true; + } + return false; + } + if (!_otherList.isEmpty()) { + switchMode(); + return true; + } + return false; + } + if (HistoryItem *item = App::histItemById(channelId(), switchId)) { + HistoryItemType itemType = item->type(); + if (itemType == HistoryItemGroup || itemType == HistoryItemCollapse) { + if (itemType == HistoryItemGroup && !_onlyImportant) return true; + if (itemType == HistoryItemCollapse && _onlyImportant) return true; + bool willNeedCollapse = (itemType == HistoryItemGroup); + + HistoryItem *prev = findPrevItem(item); + if (prev) { + fixInScrollMsgId = prev->id; + fixInScrollMsgTop = prev->y + prev->block()->y + prev->height() - height; + if (_otherList.indexOf(prev) >= 0) { + switchMode(); + insertCollapseItem(fixInScrollMsgId); + return true; + } + return false; + } + if (itemType == HistoryItemGroup) { + fixInScrollMsgId = qMax(static_cast(item)->minId(), 1); + fixInScrollMsgTop = item->y + item->block()->y - height; + if (oldLoaded && _otherOldLoaded) { + switchMode(); + insertCollapseItem(fixInScrollMsgId); + return true; + } + } else if (itemType == HistoryItemCollapse) { + fixInScrollMsgId = qMax(static_cast(item)->wasMinId(), 1); + fixInScrollMsgTop = item->y + item->block()->y - height; + if (oldLoaded && _otherOldLoaded) { + switchMode(); + return true; + } + } + return false; + } + } + LOG(("App Error: isSwitchReadyFor() switchId not found!")); + switchMode(); + return true; +} + +void ChannelHistory::getSwitchReadyFor(MsgId switchId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop) { + if (!isSwitchReadyFor(switchId, fixInScrollMsgId, fixInScrollMsgTop)) { + _otherList.clear(); + _otherNewLoaded = _otherOldLoaded = false; + + switchMode(); + } +} + +void ChannelHistory::insertCollapseItem(MsgId wasMinId) { + if (_onlyImportant) return; + + bool insertAfter = false; + for (int32 blockIndex = 0, blocksCount = blocks.size(); blockIndex < blocksCount; ++blockIndex) { + HistoryBlock *block = blocks.at(blockIndex); + for (int32 itemIndex = 0, itemsCount = block->items.size(); itemIndex < itemsCount; ++itemIndex) { + HistoryItem *item = block->items.at(itemIndex); + if (insertAfter || item->id > wasMinId || (item->id == wasMinId && !item->isImportant())) { + _collapse = new HistoryCollapse(this, block, wasMinId, item->date); + if (!addNewInTheMiddle(_collapse, blockIndex, itemIndex)) { + _collapse = 0; + } + return; + } else if (item->id == wasMinId && item->isImportant()) { + insertAfter = true; + } + } + } +} + +void ChannelHistory::switchMode() { + OtherList savedList; + savedList.reserve(msgCount); + for (Blocks::const_iterator i = blocks.cbegin(), e = blocks.cend(); i != e; ++i) { + HistoryBlock *block = *i; + for (HistoryBlock::Items::const_iterator j = block->items.cbegin(), end = block->items.cend(); j != end; ++j) { + HistoryItem *item = *j; + HistoryItemType itemType = item->type(); + if (itemType == HistoryItemMsg || itemType == HistoryItemGroup) { + savedList.push_back(item); + } + } + } + int32 savedMsgCount = msgCount; + bool savedNewLoaded = newLoaded, savedOldLoaded = oldLoaded; + + clear(true); + + newLoaded = _otherNewLoaded; + oldLoaded = _otherOldLoaded; + msgCount = _otherMsgCount; + if (int32 count = _otherList.size()) { + blocks.reserve(qCeil(count / float64(MessagesPerPage)) + 1); + createInitialDateBlock(_otherList.front()->date); + + HistoryItem *prev = 0; + for (int32 i = 0; i < count;) { + HistoryBlock *block = new HistoryBlock(this); + int32 willAddToBlock = qMin(int32(MessagesPerPage), count - i); + block->items.reserve(willAddToBlock); + for (int32 till = i + willAddToBlock; i < till; ++i) { + HistoryItem *item = _otherList.at(i); + item->attach(block); + prev = addItemAfterPrevToBlock(item, prev, block); + } + block->y = height; + blocks.push_back(block); + height += block->height; + } + } + + _otherList = savedList; + _otherNewLoaded = savedNewLoaded; + _otherOldLoaded = savedOldLoaded; + _otherMsgCount = savedMsgCount; + + _onlyImportant = !_onlyImportant; + + lastWidth = 0; +} + +HistoryGroup *ChannelHistory::findGroup(MsgId msgId) const { // find message group using binary search + if (!_onlyImportant) return findGroupInOther(msgId); + + HistoryBlock *block = findGroupBlock(msgId); + if (!block) return 0; + + int32 itemIndex = 0; + if (block->items.size() > 1) for (int32 minItem = 0, maxItem = block->items.size();;) { + for (int32 startCheckItem = (minItem + maxItem) / 2, checkItem = startCheckItem;;) { + HistoryItem *item = block->items.at(checkItem); // out msgs could be a mess in monotonic ids + if ((item->id > 0 && !item->out()) || item->type() == HistoryItemGroup) { + MsgId threshold = (item->id > 0) ? item->id : static_cast(item)->minId(); + if (threshold > msgId) { + maxItem = startCheckItem; + } else { + minItem = checkItem; + } + break; + } + if (++checkItem == maxItem) { + maxItem = startCheckItem; + break; + } + } + if (minItem + 1 == maxItem) { + itemIndex = minItem; + break; + } + } + + HistoryItem *item = block->items.at(itemIndex); + if (item->type() != HistoryItemGroup) return 0; + HistoryGroup *result = static_cast(item); + return (result->minId() < msgId && result->maxId() > msgId) ? result : 0; +} + +HistoryBlock *ChannelHistory::findGroupBlock(MsgId msgId) const { // find block with message group using binary search + if (isEmpty()) return 0; + + int32 blockIndex = 0; + if (blocks.size() > 1) for (int32 minBlock = 0, maxBlock = blocks.size();;) { + for (int32 startCheckBlock = (minBlock + maxBlock) / 2, checkBlock = startCheckBlock;;) { + HistoryBlock *block = blocks.at(checkBlock); + HistoryBlock::Items::const_iterator i = block->items.cbegin(), e = block->items.cend(); + for (; i != e; ++i) { // out msgs could be a mess in monotonic ids + if (((*i)->id > 0 && !(*i)->out()) || (*i)->type() == HistoryItemGroup) { + MsgId threshold = ((*i)->id > 0) ? (*i)->id : static_cast(*i)->minId(); + if (threshold > msgId) { + maxBlock = startCheckBlock; + } else { + minBlock = checkBlock; + } + break; + } + } + if (i != e) { + break; + } + if (++checkBlock == maxBlock) { + maxBlock = startCheckBlock; + break; + } + } + if (minBlock + 1 == maxBlock) { + blockIndex = minBlock; + break; + } + } + return blocks.at(blockIndex); +} + +HistoryGroup *ChannelHistory::findGroupInOther(MsgId msgId) const { // find message group using binary search in _otherList + if (_otherList.isEmpty()) return 0; + int32 otherIndex = 0; + if (_otherList.size() > 1) for (int32 minOther = 0, maxOther = _otherList.size();;) { + for (int32 startCheckOther = (minOther + maxOther) / 2, checkOther = startCheckOther;;) { + HistoryItem *item = _otherList.at(checkOther); // out msgs could be a mess in monotonic ids + if ((item->id > 0 && !item->out()) || item->type() == HistoryItemGroup) { + MsgId threshold = (item->id > 0) ? item->id : static_cast(item)->minId(); + if (threshold > msgId) { + maxOther = startCheckOther; + } else { + minOther = checkOther; + } + break; + } + if (++checkOther == maxOther) { + maxOther = startCheckOther; + break; + } + } + if (minOther + 1 == maxOther) { + otherIndex = minOther; + break; + } + } + HistoryItem *item = _otherList.at(otherIndex); + if (item->type() != HistoryItemGroup) return 0; + HistoryGroup *result = static_cast(item); + return (result->minId() < msgId && result->maxId() > msgId) ? result : 0; +} + +HistoryItem *ChannelHistory::findPrevItem(HistoryItem *item) const { + if (item->detached()) return 0; + int32 itemIndex = item->block()->items.indexOf(item); + int32 blockIndex = blocks.indexOf(item->block()); + if (itemIndex < 0 || blockIndex < 0) return 0; + + for (++blockIndex, ++itemIndex; blockIndex > 0;) { + --blockIndex; + HistoryBlock *block = blocks.at(blockIndex); + if (!itemIndex) itemIndex = block->items.size(); + for (; itemIndex > 0;) { + --itemIndex; + if (block->items.at(itemIndex)->type() == HistoryItemMsg) { + return block->items.at(itemIndex); + } + } + } + return 0; +} + +void ChannelHistory::messageDetached(HistoryItem *msg) { + if (_collapse == msg) { + _collapse = 0; + } +} + +void ChannelHistory::messageDeleted(HistoryItem *msg) { + int32 otherIndex = _otherList.indexOf(msg); + if (otherIndex >= 0) _otherList.removeAt(otherIndex); + if (msg->isImportant()) { // unite message groups around this important message in _otherList + if (!_onlyImportant && otherIndex > 0 && otherIndex < _otherList.size()) { + if (HistoryGroup *groupPrev = (_otherList[otherIndex - 1]->type() == HistoryItemGroup) ? static_cast(_otherList[otherIndex - 1]) : 0) { + if (HistoryGroup *groupNext = (_otherList[otherIndex]->type() == HistoryItemGroup) ? static_cast(_otherList[otherIndex]) : 0) { + groupPrev->uniteWith(groupNext); + groupNext->destroy(); + } + } + } + } else { + messageWithIdDeleted(msg->id); + } +} + +void ChannelHistory::messageWithIdDeleted(MsgId msgId) { + if (HistoryGroup *group = findGroup(msgId)) { + if (!group->decrementCount()) { + group->destroy(); + } + } +} + bool DialogsList::del(const PeerId &peerId, DialogRow *replacedBy) { RowByPeer::iterator i = rowByPeer.find(peerId); if (i == rowByPeer.cend()) return false; @@ -521,14 +826,29 @@ void DialogsIndexed::clear() { list.clear(); } +History *Histories::find(const PeerId &peerId) { + Map::const_iterator i = map.constFind(peerId); + return (i == map.cend()) ? 0 : i.value(); +} + +History *Histories::findOrInsert(const PeerId &peerId, int32 unreadCount, int32 maxInboxRead) { + Map::const_iterator i = map.constFind(peerId); + if (i == map.cend()) { + i = map.insert(peerId, peerIsChannel(peerId) ? static_cast(new ChannelHistory(peerId)) : (new History(peerId))); + i.value()->setUnreadCount(unreadCount, false); + i.value()->inboxReadBefore = maxInboxRead + 1; + } + return i.value(); +} + void Histories::clear() { App::historyClearMsgs(); - for (Parent::const_iterator i = cbegin(), e = cend(); i != e; ++i) { + for (Map::const_iterator i = map.cbegin(), e = map.cend(); i != e; ++i) { delete i.value(); } App::historyClearItems(); typing.clear(); - Parent::clear(); + map.clear(); } void Histories::regSendAction(History *history, UserData *user, const MTPSendMessageAction &action) { @@ -580,47 +900,36 @@ bool Histories::animStep(float64) { return !typing.isEmpty(); } -Histories::Parent::iterator Histories::erase(Histories::Parent::iterator i) { - typing.remove(i.value()); - delete i.value(); - return Parent::erase(i); -} - void Histories::remove(const PeerId &peer) { - iterator i = find(peer); - if (i != cend()) { - erase(i); + Map::iterator i = map.find(peer); + if (i != map.cend()) { + typing.remove(i.value()); + delete i.value(); + map.erase(i); } - } -inline bool isImportantChannelMessage(int32 flags) { - return (flags & MTPDmessage_flag_out) || (flags & MTPDmessage_flag_notify_by_from) || !(flags & MTPDmessage::flag_from_id); -} - -HistoryItem *Histories::addToBack(const MTPmessage &msg, int msgState) { +HistoryItem *Histories::addNewMessage(const MTPmessage &msg, int msgState) { int32 flags = 0; PeerId peer = peerFromMessage(msg, &flags); if (!peer) return 0; - iterator h = find(peer); - if (h == end()) { - h = insert(peer, new History(peer)); + History *h = findOrInsert(peer, 0, 0); + bool isImportant = h->isChannel() ? isImportantChannelMessage(flags) : true; + if (msgState < 0 || (!isImportant && h->asChannelHistory()->onlyImportant())) { + return h->addToHistory(msg); } - if (msgState < 0 || (peerIsChannel(peer) && !isImportantChannelMessage(flags))) { - return h.value()->addToHistory(msg); - } - if (!h.value()->loadedAtBottom()) { - HistoryItem *item = h.value()->addToHistory(msg); - if (item) { - h.value()->setLastMessage(item); + if (!h->loadedAtBottom()) { + HistoryItem *item = h->addToHistory(msg); + if (item && isImportant) { + h->setLastMessage(item); if (msgState > 0) { - h.value()->newItemAdded(item); + h->newItemAdded(item); } } return item; } - return h.value()->addToBack(msg, msgState > 0); + return h->addNewMessage(msg, msgState > 0); } HistoryItem *History::createItem(HistoryBlock *block, const MTPmessage &msg, bool newMsg, bool returnExisting) { @@ -825,89 +1134,89 @@ HistoryItem *History::createItemDocument(HistoryBlock *block, MsgId id, int32 fl return regItem(result); } -HistoryItem *History::addToBackService(MsgId msgId, QDateTime date, const QString &text, int32 flags, HistoryMedia *media, bool newMsg) { +HistoryItem *History::addNewService(MsgId msgId, QDateTime date, const QString &text, int32 flags, HistoryMedia *media, bool newMsg) { HistoryBlock *to = 0; - bool newBlock = isEmpty(); + bool newBlock = blocks.isEmpty(); if (newBlock) { to = new HistoryBlock(this); } else { - to = back(); + to = blocks.back(); } - return doAddToBack(to, newBlock, regItem(new HistoryServiceMsg(this, to, msgId, date, text, flags, media)), newMsg); + return addNewItem(to, newBlock, regItem(new HistoryServiceMsg(this, to, msgId, date, text, flags, media)), newMsg); } -HistoryItem *History::addToBack(const MTPmessage &msg, bool newMsg) { +HistoryItem *History::addNewMessage(const MTPmessage &msg, bool newMsg) { HistoryBlock *to = 0; - bool newBlock = isEmpty(); + bool newBlock = blocks.isEmpty(); if (newBlock) { to = new HistoryBlock(this); } else { - to = back(); + to = blocks.back(); } - return doAddToBack(to, newBlock, createItem(to, msg, newMsg), newMsg); + return addNewItem(to, newBlock, createItem(to, msg, newMsg), newMsg); } HistoryItem *History::addToHistory(const MTPmessage &msg) { return createItem(0, msg, false, true); } -HistoryItem *History::addToBackForwarded(MsgId id, QDateTime date, int32 from, HistoryMessage *item) { +HistoryItem *History::addNewForwarded(MsgId id, QDateTime date, int32 from, HistoryMessage *item) { HistoryBlock *to = 0; - bool newBlock = isEmpty(); + bool newBlock = blocks.isEmpty(); if (newBlock) { to = new HistoryBlock(this); } else { - to = back(); + to = blocks.back(); } - return doAddToBack(to, newBlock, createItemForwarded(to, id, date, from, item), true); + return addNewItem(to, newBlock, createItemForwarded(to, id, date, from, item), true); } -HistoryItem *History::addToBackDocument(MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc) { +HistoryItem *History::addNewDocument(MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc) { HistoryBlock *to = 0; - bool newBlock = isEmpty(); + bool newBlock = blocks.isEmpty(); if (newBlock) { to = new HistoryBlock(this); } else { - to = back(); + to = blocks.back(); } - return doAddToBack(to, newBlock, createItemDocument(to, id, flags, replyTo, date, from, doc), true); + return addNewItem(to, newBlock, createItemDocument(to, id, flags, replyTo, date, from, doc), true); } void History::createInitialDateBlock(const QDateTime &date) { HistoryBlock *dateBlock = new HistoryBlock(this); // date block HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, date); - dateBlock->push_back(dayItem); + dateBlock->items.push_back(dayItem); if (width) { int32 dh = dayItem->resize(width); dateBlock->height = dh; height += dh; - for (int32 i = 0, l = size(); i < l; ++i) { - (*this)[i]->y += dh; + for (int32 i = 0, l = blocks.size(); i < l; ++i) { + blocks[i]->y += dh; } } - push_front(dateBlock); // date block + blocks.push_front(dateBlock); } void History::addToOverview(HistoryItem *item, MediaOverviewType type) { - if (_overviewIds[type].constFind(item->id) == _overviewIds[type].cend()) { - _overview[type].push_back(item->id); - _overviewIds[type].insert(item->id, NullType()); - if (_overviewCount[type] > 0) ++_overviewCount[type]; + if (overviewIds[type].constFind(item->id) == overviewIds[type].cend()) { + overview[type].push_back(item->id); + overviewIds[type].insert(item->id, NullType()); + if (overviewCount[type] > 0) ++overviewCount[type]; if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer, type); } } bool History::addToOverviewFront(HistoryItem *item, MediaOverviewType type) { - if (_overviewIds[type].constFind(item->id) == _overviewIds[type].cend()) { - _overview[type].push_front(item->id); - _overviewIds[type].insert(item->id, NullType()); + if (overviewIds[type].constFind(item->id) == overviewIds[type].cend()) { + overview[type].push_front(item->id); + overviewIds[type].insert(item->id, NullType()); return true; } return false; } -HistoryItem *History::doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem *adding, bool newMsg) { +HistoryItem *History::addNewItem(HistoryBlock *to, bool newBlock, HistoryItem *adding, bool newMsg) { if (!adding) { if (newBlock) delete to; return adding; @@ -917,10 +1226,10 @@ HistoryItem *History::doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem * createInitialDateBlock(adding->date); to->y = height; - push_back(to); - } else if (to->back()->date.date() != adding->date.date()) { + blocks.push_back(to); + } else if (to->items.back()->date.date() != adding->date.date()) { HistoryItem *dayItem = createDayServiceMsg(this, to, adding->date); - to->push_back(dayItem); + to->items.push_back(dayItem); dayItem->y = to->height; if (width) { int32 dh = dayItem->resize(width); @@ -928,7 +1237,7 @@ HistoryItem *History::doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem * height += dh; } } - to->push_back(adding); + to->items.push_back(adding); setLastMessage(adding); adding->y = to->height; @@ -1027,64 +1336,96 @@ void History::newItemAdded(HistoryItem *item) { } } -void History::addToFront(const QVector &slice) { +HistoryItem *History::addItemAfterPrevToBlock(HistoryItem *item, HistoryItem *prev, HistoryBlock *block) { + if (prev && prev->date.date() != item->date.date()) { + HistoryItem *dayItem = createDayServiceMsg(this, prev->block(), item->date); + prev->block()->items.push_back(dayItem); + if (width) { + dayItem->y = prev->block()->height; + prev->block()->height += dayItem->resize(width); + if (prev->block() != block) { + height += dayItem->height(); + } + } + } + block->items.push_back(item); + if (width) { + item->y = block->height; + block->height += item->resize(width); + } + return item; +} + +HistoryItem *History::addMessageGroupAfterPrevToBlock(const MTPDmessageGroup &group, const QDateTime &date, HistoryItem *prev, HistoryBlock *block) { + if (prev && prev->type() == HistoryItemGroup) { + static_cast(prev)->uniteWith(group.vmin_id.v, group.vmax_id.v, group.vcount.v); + return prev; + } + return addItemAfterPrevToBlock(regItem(new HistoryGroup(this, block, group, date)), prev, block); +} + +void History::addOlderSlice(const QVector &slice, const QVector *collapsed) { if (slice.isEmpty()) { oldLoaded = true; - return; + if (!collapsed || collapsed->isEmpty() || !isChannel()) return; } + ChannelHistory *channel = collapsed ? asChannelHistory() : 0; + const MTPMessageGroup *groupsBegin = channel ? collapsed->constData() : 0, *groupsIt = groupsBegin, *groupsEnd = channel ? (groupsBegin + collapsed->size()) : 0; + int32 addToH = 0, skip = 0; - if (!isEmpty()) { - if (width) addToH = -front()->height; - pop_front(); // remove date block + if (!blocks.isEmpty()) { // remove date block + if (width) addToH = -blocks.front()->height; + delete blocks.front(); + blocks.pop_front(); } - HistoryItem *till = isEmpty() ? 0 : front()->front(), *prev = 0; + HistoryItem *till = blocks.isEmpty() ? 0 : blocks.front()->items.front(), *prev = 0; HistoryBlock *block = new HistoryBlock(this); - block->reserve(slice.size()); - int32 wasMsgCount = msgCount; - for (QVector::const_iterator i = slice.cend() - 1, e = slice.cbegin(); ; --i) { + block->items.reserve(slice.size() + (collapsed ? collapsed->size() : 0)); + for (QVector::const_iterator i = slice.cend(), e = slice.cbegin(); i != e;) { + --i; HistoryItem *adding = createItem(block, *i, false); - if (adding) { - if (prev && prev->date.date() != adding->date.date()) { - HistoryItem *dayItem = createDayServiceMsg(this, block, adding->date); - block->push_back(dayItem); - if (width) { - dayItem->y = block->height; - block->height += dayItem->resize(width); - } - } - block->push_back(adding); - if (width) { - adding->y = block->height; - block->height += adding->resize(width); - } - setMsgCount(msgCount + 1); - prev = adding; + if (!adding) continue; + + for (; groupsIt != groupsEnd; ++groupsIt) { + if (groupsIt->type() != mtpc_messageGroup) continue; + const MTPDmessageGroup &group(groupsIt->c_messageGroup()); + if (group.vmin_id.v >= adding->id) break; + + prev = addMessageGroupAfterPrevToBlock(group, (prev ? prev : adding)->date, prev, block); } - if (i == e) break; + + prev = addItemAfterPrevToBlock(adding, prev, block); + setMsgCount(msgCount + 1); + } + for (; groupsIt != groupsEnd; ++groupsIt) { + if (groupsIt->type() != mtpc_messageGroup) continue; + const MTPDmessageGroup &group(groupsIt->c_messageGroup()); + + prev = addMessageGroupAfterPrevToBlock(group, prev ? prev->date : date(group.vdate), prev, block); + } + + while (till && prev && till->type() == HistoryItemGroup && prev->type() == HistoryItemGroup) { + static_cast(prev)->uniteWith(static_cast(till)); + till->detach(); + delete till; + if (blocks.front()->items.isEmpty()) { + delete blocks.front(); + blocks.pop_front(); + } + till = blocks.isEmpty() ? 0 : blocks.front()->items.front(); } if (till && prev && prev->date.date() != till->date.date()) { HistoryItem *dayItem = createDayServiceMsg(this, block, till->date); - block->push_back(dayItem); + block->items.push_back(dayItem); if (width) { dayItem->y = block->height; block->height += dayItem->resize(width); } } - if (block->size()) { - if (loadedAtBottom() && wasMsgCount < unreadCount && msgCount >= unreadCount) { - for (int32 i = block->size(); i > 0; --i) { - if ((*block)[i - 1]->itemType() == HistoryItem::MsgType) { - ++wasMsgCount; - if (wasMsgCount == unreadCount) { - showFrom = (*block)[i - 1]; - break; - } - } - } - } - push_front(block); + if (!block->items.isEmpty()) { + blocks.push_front(block); if (width) { addToH += block->height; ++skip; @@ -1093,8 +1434,8 @@ void History::addToFront(const QVector &slice) { if (loadedAtBottom()) { // add photos to overview and authors to lastAuthors int32 mask = 0; QList *lastAuthors = peer->isChat() ? &(peer->asChat()->lastAuthors) : 0; - for (int32 i = block->size(); i > 0; --i) { - HistoryItem *item = (*block)[i - 1]; + for (int32 i = block->items.size(); i > 0; --i) { + HistoryItem *item = block->items[i - 1]; HistoryMedia *media = item->getMedia(true); if (media) { HistoryMediaType mt = media->type(); @@ -1158,23 +1499,23 @@ void History::addToFront(const QVector &slice) { } else { delete block; } - if (!isEmpty()) { + if (!blocks.isEmpty()) { HistoryBlock *dateBlock = new HistoryBlock(this); - HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, front()->front()->date); - dateBlock->push_back(dayItem); + HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, blocks.front()->items.front()->date); + dateBlock->items.push_back(dayItem); if (width) { int32 dh = dayItem->resize(width); dateBlock->height = dh; if (skip) { - front()->y += dh; + blocks.front()->y += dh; } addToH += dh; ++skip; } - push_front(dateBlock); // date block + blocks.push_front(dateBlock); // date block } if (width && addToH) { - for (iterator i = begin(), e = end(); i != e; ++i) { + for (Blocks::iterator i = blocks.begin(), e = blocks.end(); i != e; ++i) { if (skip) { --skip; } else { @@ -1185,44 +1526,48 @@ void History::addToFront(const QVector &slice) { } } -void History::addToBack(const QVector &slice) { +void History::addNewerSlice(const QVector &slice, const QVector *collapsed) { if (slice.isEmpty()) { newLoaded = true; - return; + if (!collapsed || collapsed->isEmpty() || !isChannel()) return; } + ChannelHistory *channel = collapsed ? asChannelHistory() : 0; + const MTPMessageGroup *groupsBegin = channel ? collapsed->constData() : 0, *groupsIt = groupsBegin, *groupsEnd = channel ? (groupsBegin + collapsed->size()) : 0; + bool wasEmpty = isEmpty(); - HistoryItem *prev = isEmpty() ? 0 : back()->back(); + HistoryItem *prev = blocks.isEmpty() ? 0 : blocks.back()->items.back(); HistoryBlock *block = new HistoryBlock(this); - block->reserve(slice.size()); - int32 wasMsgCount = msgCount; + block->items.reserve(slice.size() + (collapsed ? collapsed->size() : 0)); for (QVector::const_iterator i = slice.cend(), e = slice.cbegin(); i != e;) { --i; HistoryItem *adding = createItem(block, *i, false); - if (adding) { - if (prev && prev->date.date() != adding->date.date()) { - HistoryItem *dayItem = createDayServiceMsg(this, block, adding->date); - prev->block()->push_back(dayItem); - dayItem->y = prev->block()->height; - prev->block()->height += dayItem->resize(width); - if (prev->block() != block) { - height += dayItem->height(); - } - } - block->push_back(adding); - adding->y = block->height; - block->height += adding->resize(width); - setMsgCount(msgCount + 1); - prev = adding; + if (!adding) continue; + + for (; groupsIt != groupsEnd; ++groupsIt) { + if (groupsIt->type() != mtpc_messageGroup) continue; + const MTPDmessageGroup &group(groupsIt->c_messageGroup()); + if (group.vmin_id.v >= adding->id) break; + + prev = addMessageGroupAfterPrevToBlock(group, (prev ? prev : adding)->date, prev, block); } - if (i == e) break; + + prev = addItemAfterPrevToBlock(adding, prev, block); + setMsgCount(msgCount + 1); } + for (; groupsIt != groupsEnd; ++groupsIt) { + if (groupsIt->type() != mtpc_messageGroup) continue; + const MTPDmessageGroup &group(groupsIt->c_messageGroup()); + + prev = addMessageGroupAfterPrevToBlock(group, prev ? prev->date : date(group.vdate), prev, block); + } + bool wasLoadedAtBottom = loadedAtBottom(); - if (block->size()) { + if (block->items.size()) { block->y = height; - push_back(block); + blocks.push_back(block); height += block->height; } else { newLoaded = true; @@ -1232,17 +1577,17 @@ void History::addToBack(const QVector &slice) { if (!wasLoadedAtBottom && loadedAtBottom()) { // add all loaded photos to overview int32 mask = 0; for (int32 i = 0; i < OverviewCount; ++i) { - if (_overviewCount[i] == 0) continue; // all loaded - if (!_overview[i].isEmpty() || !_overviewIds[i].isEmpty()) { - _overview[i].clear(); - _overviewIds[i].clear(); + if (overviewCount[i] == 0) continue; // all loaded + if (!overview[i].isEmpty() || !overviewIds[i].isEmpty()) { + overview[i].clear(); + overviewIds[i].clear(); mask |= (1 << i); } } - for (int32 i = 0; i < size(); ++i) { - HistoryBlock *b = (*this)[i]; - for (int32 j = 0; j < b->size(); ++j) { - HistoryItem *item = (*b)[j]; + for (int32 i = 0; i < blocks.size(); ++i) { + HistoryBlock *b = blocks[i]; + for (int32 j = 0; j < b->items.size(); ++j) { + HistoryItem *item = b->items[j]; HistoryMedia *media = item->getMedia(true); if (media) { HistoryMediaType mt = media->type(); @@ -1250,15 +1595,15 @@ void History::addToBack(const QVector &slice) { if (t != OverviewCount) { if (mt == MediaTypeDocument && static_cast(media)->document()->song()) { t = OverviewAudioDocuments; - if (_overviewCount[t] != 0) { - _overview[t].push_back(item->id); - _overviewIds[t].insert(item->id, NullType()); + if (overviewCount[t] != 0) { + overview[t].push_back(item->id); + overviewIds[t].insert(item->id, NullType()); mask |= (1 << t); } } else { - if (_overviewCount[t] != 0) { - _overview[t].push_back(item->id); - _overviewIds[t].insert(item->id, NullType()); + if (overviewCount[t] != 0) { + overview[t].push_back(item->id); + overviewIds[t].insert(item->id, NullType()); mask |= (1 << t); } } @@ -1266,9 +1611,9 @@ void History::addToBack(const QVector &slice) { } if (item->hasTextLinks()) { MediaOverviewType t = OverviewLinks; - if (_overviewCount[t] != 0) { - _overview[t].push_back(item->id); - _overviewIds[t].insert(item->id, NullType()); + if (overviewCount[t] != 0) { + overview[t].push_back(item->id); + overviewIds[t].insert(item->id, NullType()); mask |= (1 << t); } } @@ -1280,23 +1625,23 @@ void History::addToBack(const QVector &slice) { } if (wasEmpty && !isEmpty()) { HistoryBlock *dateBlock = new HistoryBlock(this); - HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, front()->front()->date); - dateBlock->push_back(dayItem); + HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, blocks.front()->items.front()->date); + dateBlock->items.push_back(dayItem); int32 dh = dayItem->resize(width); dateBlock->height = dh; - for (iterator i = begin(), e = end(); i != e; ++i) { + for (Blocks::iterator i = blocks.begin(), e = blocks.end(); i != e; ++i) { (*i)->y += dh; } - push_front(dateBlock); // date block + blocks.push_front(dateBlock); // date block height += dh; } } int32 History::countUnread(MsgId upTo) { int32 result = 0; - for (const_iterator i = cend(), e = cbegin(); i != e;) { + for (Blocks::const_iterator i = blocks.cend(), e = blocks.cbegin(); i != e;) { --i; - for (HistoryBlock::const_iterator j = (*i)->cend(), en = (*i)->cbegin(); j != en;) { + for (HistoryBlock::Items::const_iterator j = (*i)->items.cend(), en = (*i)->items.cbegin(); j != en;) { --j; if ((*j)->id > 0 && (*j)->id <= upTo) { break; @@ -1308,6 +1653,24 @@ int32 History::countUnread(MsgId upTo) { return result; } +void History::updateShowFrom() { + if (showFrom) return; + + for (Blocks::const_iterator i = blocks.cend(); i != blocks.cbegin();) { + --i; + for (HistoryBlock::Items::const_iterator j = (*i)->items.cend(); j != (*i)->items.cbegin();) { + --j; + if ((*j)->type() == HistoryItemMsg && (*j)->id > 0) { + if ((*j)->id >= inboxReadBefore) { + showFrom = *j; + } else { + return; + } + } + } + } +} + MsgId History::inboxRead(MsgId upTo) { if (unreadCount) { if (upTo && loadedAtBottom()) App::main()->historyToDown(this); @@ -1345,7 +1708,7 @@ MsgId History::outboxRead(HistoryItem *wasRead) { void History::setUnreadCount(int32 newUnreadCount, bool psUpdate) { if (unreadCount != newUnreadCount) { if (newUnreadCount == 1) { - if (loadedAtBottom()) showFrom = isEmpty() ? 0 : back()->back(); + if (loadedAtBottom()) showFrom = isEmpty() ? 0 : blocks.back()->items.back(); inboxReadBefore = qMax(inboxReadBefore, msgIdForRead()); } else if (!newUnreadCount) { showFrom = 0; @@ -1376,22 +1739,22 @@ void History::setMsgCount(int32 newMsgCount) { void History::getNextShowFrom(HistoryBlock *block, int32 i) { if (i >= 0) { - int32 l = block->size(); + int32 l = block->items.size(); for (++i; i < l; ++i) { - if ((*block)[i]->itemType() == HistoryItem::MsgType) { - showFrom = (*block)[i]; + if (block->items[i]->type() == HistoryItemMsg) { + showFrom = block->items[i]; return; } } } - int32 j = indexOf(block), s = size(); + int32 j = blocks.indexOf(block), s = blocks.size(); if (j >= 0) { for (++j; j < s; ++j) { - block = (*this)[j]; - for (int32 i = 0, l = block->size(); i < l; ++i) { - if ((*block)[i]->itemType() == HistoryItem::MsgType) { - showFrom = (*block)[i]; + block = blocks[j]; + for (int32 i = 0, l = block->items.size(); i < l; ++i) { + if (block->items[i]->type() == HistoryItemMsg) { + showFrom = block->items[i]; return; } } @@ -1402,27 +1765,39 @@ void History::getNextShowFrom(HistoryBlock *block, int32 i) { void History::addUnreadBar() { if (unreadBar || !showFrom || showFrom->detached() || !unreadCount) return; - + HistoryBlock *block = showFrom->block(); - int32 i = block->indexOf(showFrom); - int32 j = indexOf(block); - if (i < 0 || j < 0) return; - - HistoryUnreadBar *bar = new HistoryUnreadBar(this, block, unreadCount, showFrom->date); - block->insert(i, bar); - unreadBar = bar; - - unreadBar->y = showFrom->y; - - int32 dh = unreadBar->resize(width), l = block->size(); - for (++i; i < l; ++i) { - (*block)[i]->y += dh; + unreadBar = new HistoryUnreadBar(this, block, unreadCount, showFrom->date); + if (!addNewInTheMiddle(unreadBar, blocks.indexOf(block), block->items.indexOf(showFrom))) { + unreadBar = 0; } - block->height += dh; - for (++j, l = size(); j < l; ++j) { - (*this)[j]->y += dh; +} + +HistoryItem *History::addNewInTheMiddle(HistoryItem *newItem, int32 blockIndex, int32 itemIndex) { + if (!regItem(newItem)) { + return 0; } - height += dh; + if (blockIndex < 0 || itemIndex < 0 || blockIndex >= blocks.size() || itemIndex >= blocks.at(blockIndex)->items.size()) { + delete newItem; + return 0; + } + + HistoryBlock *block = blocks.at(blockIndex); + newItem->y = block->items.at(itemIndex)->y; + block->items.insert(itemIndex, newItem); + + if (width) { + int32 dh = newItem->resize(width), l = block->items.size(); + for (++itemIndex; itemIndex < l; ++itemIndex) { + block->items[itemIndex]->y += dh; + } + block->height += dh; + for (++blockIndex, l = blocks.size(); blockIndex < l; ++blockIndex) { + blocks[blockIndex]->y += dh; + } + height += dh; + } + return newItem; } void History::clearNotifications() { @@ -1437,34 +1812,37 @@ bool History::loadedAtTop() const { return oldLoaded; } -bool History::isReadyFor(MsgId msgId, bool check) const { +bool History::isReadyFor(MsgId msgId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop) { + if (msgId != ShowAtTheEndMsgId && msgId != ShowAtUnreadMsgId && msgId < 0 && isChannel()) { + return asChannelHistory()->isSwitchReadyFor(msgId, fixInScrollMsgId, fixInScrollMsgTop); + } + fixInScrollMsgId = 0; + fixInScrollMsgTop = 0; if (msgId == ShowAtTheEndMsgId) { return loadedAtBottom(); - } else if (check) { - if (msgId == ShowAtUnreadMsgId) { - if (unreadCount) { - if (!isEmpty()) { - return (loadedAtTop() || minMsgId() <= inboxReadBefore) && (loadedAtBottom() || maxMsgId() >= inboxReadBefore); - } else { - return false; - } - } else { - return loadedAtBottom(); - } - } - HistoryItem *item = App::histItemById(channelId(), msgId); - return item && item->history() == this && !item->detached(); } - return !isEmpty(); + if (msgId == ShowAtUnreadMsgId) { + if (unreadCount) { + if (!isEmpty()) { + return (loadedAtTop() || minMsgId() <= inboxReadBefore) && (loadedAtBottom() || maxMsgId() >= inboxReadBefore); + } + return false; + } + return loadedAtBottom(); + } + HistoryItem *item = App::histItemById(channelId(), msgId); + return item && (item->history() == this) && !item->detached(); } -void History::getReadyFor(MsgId msgId) { - if (!isReadyFor(msgId, true)) { +void History::getReadyFor(MsgId msgId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop) { + if (msgId != ShowAtTheEndMsgId && msgId != ShowAtUnreadMsgId && msgId < 0 && isChannel()) { + return asChannelHistory()->getSwitchReadyFor(msgId, fixInScrollMsgId, fixInScrollMsgTop); + } + if (!isReadyFor(msgId, fixInScrollMsgId, fixInScrollMsgTop)) { clear(true); newLoaded = (msgId == ShowAtTheEndMsgId); oldLoaded = false; lastWidth = 0; - lastShowAtMsgId = msgId; } } @@ -1503,18 +1881,15 @@ void History::fixLastMessage(bool wasAtBottom) { wasAtBottom = false; } if (wasAtBottom) { - setLastMessage(back()->back(), false); + setLastMessage(blocks.back()->items.back(), false); } else { setLastMessage(0); - if (App::main()) { - App::main()->checkPeerHistory(peer); - } } } MsgId History::minMsgId() const { - for (const_iterator i = cbegin(), e = cend(); i != e; ++i) { - for (HistoryBlock::const_iterator j = (*i)->cbegin(), en = (*i)->cend(); j != en; ++j) { + for (Blocks::const_iterator i = blocks.cbegin(), e = blocks.cend(); i != e; ++i) { + for (HistoryBlock::Items::const_iterator j = (*i)->items.cbegin(), en = (*i)->items.cend(); j != en; ++j) { if ((*j)->id > 0) { return (*j)->id; } @@ -1524,9 +1899,9 @@ MsgId History::minMsgId() const { } MsgId History::maxMsgId() const { - for (const_iterator i = cend(), e = cbegin(); i != e;) { + for (Blocks::const_iterator i = blocks.cend(), e = blocks.cbegin(); i != e;) { --i; - for (HistoryBlock::const_iterator j = (*i)->cend(), en = (*i)->cbegin(); j != en;) { + for (HistoryBlock::Items::const_iterator j = (*i)->items.cend(), en = (*i)->items.cbegin(); j != en;) { --j; if ((*j)->id > 0) { return (*j)->id; @@ -1546,7 +1921,7 @@ int32 History::geomResize(int32 newWidth, int32 *ytransform, HistoryItem *resize if (width != newWidth) resizedItem = 0; // recount all items if (width != newWidth || resizedItem) { int32 y = 0; - for (iterator i = begin(), e = end(); i != e; ++i) { + for (Blocks::iterator i = blocks.begin(), e = blocks.end(); i != e; ++i) { HistoryBlock *block = *i; bool updTransform = ytransform && (*ytransform >= block->y) && (*ytransform < block->y + block->height); if (updTransform) *ytransform -= block->y; @@ -1565,6 +1940,14 @@ int32 History::geomResize(int32 newWidth, int32 *ytransform, HistoryItem *resize return height; } +ChannelHistory *History::asChannelHistory() { + return isChannel() ? static_cast(this) : 0; +} + +const ChannelHistory *History::asChannelHistory() const { + return isChannel() ? static_cast(this) : 0; +} + void History::clear(bool leaveItems) { if (unreadBar) { unreadBar->destroy(); @@ -1576,24 +1959,24 @@ void History::clear(bool leaveItems) { setLastMessage(0); } for (int32 i = 0; i < OverviewCount; ++i) { - if (!_overview[i].isEmpty() || !_overviewIds[i].isEmpty()) { + if (!overview[i].isEmpty() || !overviewIds[i].isEmpty()) { if (leaveItems) { - if (_overviewCount[i] == 0) _overviewCount[i] = _overview[i].size(); + if (overviewCount[i] == 0) overviewCount[i] = overview[i].size(); } else { - _overviewCount[i] = -1; // not loaded yet + overviewCount[i] = -1; // not loaded yet } - _overview[i].clear(); - _overviewIds[i].clear(); + overview[i].clear(); + overviewIds[i].clear(); if (App::wnd() && !App::quiting()) App::wnd()->mediaOverviewUpdated(peer, MediaOverviewType(i)); } } - for (Parent::const_iterator i = cbegin(), e = cend(); i != e; ++i) { + for (Blocks::const_iterator i = blocks.cbegin(), e = blocks.cend(); i != e; ++i) { if (leaveItems) { (*i)->clear(true); } delete *i; } - Parent::clear(); + blocks.clear(); setMsgCount(0); if (leaveItems) { lastKeyboardInited = false; @@ -1609,32 +1992,27 @@ void History::clear(bool leaveItems) { if (leaveItems && App::main()) App::main()->historyCleared(this); } -History::Parent::iterator History::erase(History::Parent::iterator i) { - delete *i; - return Parent::erase(i); -} - void History::blockResized(HistoryBlock *block, int32 dh) { - int32 i = indexOf(block), l = size(); + int32 i = blocks.indexOf(block), l = blocks.size(); if (i >= 0) { for (++i; i < l; ++i) { - (*this)[i]->y -= dh; + blocks[i]->y -= dh; } height -= dh; } } void History::removeBlock(HistoryBlock *block) { - int32 i = indexOf(block), h = block->height; + int32 i = blocks.indexOf(block), h = block->height; if (i >= 0) { - removeAt(i); - int32 l = size(); + blocks.removeAt(i); + int32 l = blocks.size(); if (i > 0 && l == 1) { // only fake block with date left - removeBlock((*this)[0]); + removeBlock(blocks[0]); height = 0; } else if (h) { for (; i < l; ++i) { - (*this)[i]->y -= h; + blocks[i]->y -= h; } height -= h; } @@ -1644,7 +2022,7 @@ void History::removeBlock(HistoryBlock *block) { int32 HistoryBlock::geomResize(int32 newWidth, int32 *ytransform, HistoryItem *resizedItem) { int32 y = 0; - for (iterator i = begin(), e = end(); i != e; ++i) { + for (Items::iterator i = items.begin(), e = items.end(); i != e; ++i) { HistoryItem *item = *i; bool updTransform = ytransform && (*ytransform >= item->y) && (*ytransform < item->y + item->height()); if (updTransform) *ytransform -= item->y; @@ -1665,24 +2043,19 @@ int32 HistoryBlock::geomResize(int32 newWidth, int32 *ytransform, HistoryItem *r void HistoryBlock::clear(bool leaveItems) { if (leaveItems) { - for (Parent::const_iterator i = cbegin(), e = cend(); i != e; ++i) { + for (Items::const_iterator i = items.cbegin(), e = items.cend(); i != e; ++i) { (*i)->detachFast(); } } else { - for (Parent::const_iterator i = cbegin(), e = cend(); i != e; ++i) { + for (Items::const_iterator i = items.cbegin(), e = items.cend(); i != e; ++i) { delete *i; } } - Parent::clear(); -} - -HistoryBlock::Parent::iterator HistoryBlock::erase(HistoryBlock::Parent::iterator i) { - delete *i; - return Parent::erase(i); + items.clear(); } void HistoryBlock::removeItem(HistoryItem *item) { - int32 i = indexOf(item), dh = 0; + int32 i = items.indexOf(item), dh = 0; if (history->showFrom == item) { history->getNextShowFrom(this, i); } @@ -1692,19 +2065,73 @@ void HistoryBlock::removeItem(HistoryItem *item) { bool createInitialDate = false; QDateTime initialDateTime; - int32 myIndex = history->indexOf(this); - if (myIndex >= 0 && item->itemType() != HistoryItem::DateType) { // fix date items - HistoryItem *nextItem = (i < size() - 1) ? (*this)[i + 1] : ((myIndex < history->size() - 1) ? (*(*history)[myIndex + 1])[0] : 0); + int32 myIndex = history->blocks.indexOf(this); + if (myIndex >= 0 && item->type() != HistoryItemDate) { // fix message groups and date items + if (item->isImportant()) { // unite message groups around this important message + HistoryGroup *nextGroup = 0, *prevGroup = 0; + HistoryCollapse *nextCollapse = 0; + HistoryItem *prevItem = 0; + for (int32 nextBlock = myIndex, nextIndex = qMin(items.size(), i + 1); nextBlock < history->blocks.size(); ++nextBlock) { + HistoryBlock *block = history->blocks.at(nextBlock); + for (; nextIndex < block->items.size(); ++nextIndex) { + HistoryItem *item = block->items.at(nextIndex); + if (item->type() == HistoryItemMsg) { + break; + } else if (item->type() == HistoryItemGroup) { + nextGroup = static_cast(item); + break; + } else if (item->type() == HistoryItemCollapse) { + nextCollapse = static_cast(item); + break; + } + } + if (nextIndex == block->items.size()) { + nextIndex = 0; + } else { + break; + } + } + for (int32 prevBlock = myIndex + 1, prevIndex = qMax(1, i); prevBlock > 0;) { + --prevBlock; + HistoryBlock *block = history->blocks.at(prevBlock); + if (!prevIndex) prevIndex = block->items.size(); + for (; prevIndex > 0;) { + --prevIndex; + HistoryItem *item = block->items.at(prevIndex); + if (item->type() == HistoryItemMsg || item->type() == HistoryItemCollapse) { + prevItem = item; + ++prevIndex; + break; + } else if (item->type() == HistoryItemGroup) { + prevGroup = static_cast(item); + ++prevIndex; + break; + } + } + if (prevIndex != 0) { + break; + } + } + if (nextGroup && prevGroup) { + prevGroup->uniteWith(nextGroup); + nextGroup->destroy(); + } else if (nextCollapse && (!prevItem || !prevItem->isImportant())) { + nextCollapse->destroy(); + } + } + + // fix date items + HistoryItem *nextItem = (i < items.size() - 1) ? items[i + 1] : ((myIndex < history->blocks.size() - 1) ? history->blocks[myIndex + 1]->items[0] : 0); if (nextItem && nextItem == history->unreadBar) { // skip unread bar - if (i < size() - 2) { - nextItem = (*this)[i + 2]; - } else if (i < size() - 1) { - nextItem = ((myIndex < history->size() - 1) ? (*(*history)[myIndex + 1])[0] : 0); - } else if (myIndex < history->size() - 1) { - if (0 < (*history)[myIndex + 1]->size() - 1) { - nextItem = (*(*history)[myIndex + 1])[1]; - } else if (myIndex < history->size() - 2) { - nextItem = (*(*history)[myIndex + 2])[0]; + if (i < items.size() - 2) { + nextItem = items[i + 2]; + } else if (i < items.size() - 1) { + nextItem = ((myIndex < history->blocks.size() - 1) ? history->blocks[myIndex + 1]->items[0] : 0); + } else if (myIndex < history->blocks.size() - 1) { + if (0 < history->blocks[myIndex + 1]->items.size() - 1) { + nextItem = history->blocks[myIndex + 1]->items[1]; + } else if (myIndex < history->blocks.size() - 2) { + nextItem = history->blocks[myIndex + 2]->items[0]; } else { nextItem = 0; } @@ -1712,22 +2139,22 @@ void HistoryBlock::removeItem(HistoryItem *item) { nextItem = 0; } } - if (!nextItem || nextItem->itemType() == HistoryItem::DateType) { // only if there is no next item or it is a date item - HistoryItem *prevItem = (i > 0) ? (*this)[i - 1] : 0; + if (!nextItem || nextItem->type() == HistoryItemDate) { // only if there is no next item or it is a date item + HistoryItem *prevItem = (i > 0) ? items[i - 1] : 0; if (prevItem && prevItem == history->unreadBar) { // skip unread bar - prevItem = (i > 1) ? (*this)[i - 2] : 0; + prevItem = (i > 1) ? items[i - 2] : 0; } if (prevItem) { - if (prevItem->itemType() == HistoryItem::DateType) { + if (prevItem->type() == HistoryItemDate) { prevItem->destroy(); --i; } } else if (myIndex > 0) { - HistoryBlock *prevBlock = (*history)[myIndex - 1]; - if (prevBlock->isEmpty() || ((myIndex == 1) && (prevBlock->size() != 1 || (*prevBlock->cbegin())->itemType() != HistoryItem::DateType))) { - LOG(("App Error: Found bad history, with no first date block: %1").arg((*history)[0]->size())); - } else if ((*prevBlock)[prevBlock->size() - 1]->itemType() == HistoryItem::DateType) { - (*prevBlock)[prevBlock->size() - 1]->destroy(); + HistoryBlock *prevBlock = history->blocks[myIndex - 1]; + if (prevBlock->items.isEmpty() || ((myIndex == 1) && (prevBlock->items.size() != 1 || prevBlock->items.front()->type() != HistoryItemDate))) { + LOG(("App Error: Found bad history, with no first date block: %1").arg(history->blocks[0]->items.size())); + } else if (prevBlock->items[prevBlock->items.size() - 1]->type() == HistoryItemDate) { + prevBlock->items[prevBlock->items.size() - 1]->destroy(); if (nextItem && myIndex == 1) { // destroy next date (for creating initial then) initialDateTime = nextItem->date; createInitialDate = true; @@ -1740,15 +2167,15 @@ void HistoryBlock::removeItem(HistoryItem *item) { // myIndex can be invalid now, because of destroying previous blocks dh = item->height(); - remove(i); - int32 l = size(); + items.remove(i); + int32 l = items.size(); if ((!item->out() || item->fromChannel()) && item->unread() && history->unreadCount) { history->setUnreadCount(history->unreadCount - 1); } - int32 itemType = item->itemType(); - if (itemType == HistoryItem::MsgType) { + int32 itemType = item->type(); + if (itemType == HistoryItemMsg) { history->setMsgCount(history->msgCount - 1); - } else if (itemType == HistoryItem::UnreadBarType) { + } else if (itemType == HistoryItemUnreadBar) { if (history->unreadBar == item) { history->unreadBar = 0; } @@ -1759,7 +2186,7 @@ void HistoryBlock::removeItem(HistoryItem *item) { History *h = history; if (l) { for (; i < l; ++i) { - (*this)[i]->y -= dh; + items[i]->y -= dh; } height -= dh; history->blockResized(this, dh); @@ -1819,6 +2246,9 @@ void HistoryItem::destroy() { bool wasAtBottom = history()->loadedAtBottom(); _history->removeNotification(this); detach(); + if (history()->isChannel()) { + history()->asChannelHistory()->messageDeleted(this); + } if (history()->lastMsg == this) { history()->fixLastMessage(wasAtBottom); } @@ -1838,8 +2268,13 @@ void HistoryItem::destroy() { } void HistoryItem::detach() { - if (_history && _history->unreadBar == this) { - _history->unreadBar = 0; + if (_history) { + if (_history->unreadBar == this) { + _history->unreadBar = 0; + } + if (_history->isChannel()) { + _history->asChannelHistory()->messageDetached(this); + } } if (_block) { _block->removeItem(this); @@ -1850,7 +2285,7 @@ void HistoryItem::detach() { _history->showFrom = 0; } } - if (_history && _history->unreadBar && _history->back()->back() == _history->unreadBar) { + if (_history && _history->unreadBar && _history->blocks.back()->items.back() == _history->unreadBar) { _history->unreadBar->destroy(); } } @@ -2141,7 +2576,11 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, bool selected, in fwdFrom = st::msgPadding.top() + st::msgNameFont->height; skipy += replyFrom; p.setFont(st::msgNameFont->f); - p.setPen(parent->from()->color->p); + if (fromChannel) { + p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor); + } else { + p.setPen(parent->from()->color); + } parent->from()->nameText.drawElided(p, st::mediaPadding.left(), st::msgPadding.top(), width - st::mediaPadding.left() - st::mediaPadding.right()); if (!fwd && !reply) skipy += st::msgPadding.top(); } else if (!reply) { @@ -2525,7 +2964,11 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, bool selected, in if (parent->displayFromName()) { p.setFont(st::msgNameFont->f); - p.setPen(parent->from()->color->p); + if (fromChannel) { + p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor); + } else { + p.setPen(parent->from()->color); + } parent->from()->nameText.drawElided(p, st::mediaPadding.left(), st::msgPadding.top(), width - st::mediaPadding.left() - st::mediaPadding.right()); } if (reply) { @@ -2738,7 +3181,11 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in if (parent->displayFromName()) { p.setFont(st::msgNameFont->f); - p.setPen(parent->from()->color->p); + if (fromChannel) { + p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor); + } else { + p.setPen(parent->from()->color); + } parent->from()->nameText.drawElided(p, st::mediaPadding.left(), st::msgPadding.top(), width - st::mediaPadding.left() - st::mediaPadding.right()); } if (reply) { @@ -3072,7 +3519,11 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, if (parent->displayFromName()) { p.setFont(st::msgNameFont->f); - p.setPen(parent->from()->color->p); + if (fromChannel) { + p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor); + } else { + p.setPen(parent->from()->color); + } parent->from()->nameText.drawElided(p, st::mediaPadding.left(), st::msgPadding.top(), width - st::mediaPadding.left() - st::mediaPadding.right()); } if (reply) { @@ -3742,7 +4193,11 @@ void HistoryContact::draw(Painter &p, const HistoryItem *parent, bool selected, if (parent->displayFromName()) { p.setFont(st::msgNameFont->f); - p.setPen(parent->from()->color->p); + if (fromChannel) { + p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor); + } else { + p.setPen(parent->from()->color); + } parent->from()->nameText.drawElided(p, st::mediaPadding.left(), st::msgPadding.top(), width - st::mediaPadding.left() - st::mediaPadding.right()); } if (reply) { @@ -4887,7 +5342,11 @@ void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, bool selected fwdFrom = st::msgPadding.top() + st::msgNameFont->height; skipy += replyFrom; p.setFont(st::msgNameFont->f); - p.setPen(parent->from()->color->p); + if (fromChannel) { + p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor); + } else { + p.setPen(parent->from()->color); + } parent->from()->nameText.drawElided(p, st::mediaPadding.left(), st::msgPadding.top(), width - st::mediaPadding.left() - st::mediaPadding.right()); if (!fwd && !reply) skipy += st::msgPadding.top(); } else if (!reply) { @@ -5157,7 +5616,7 @@ HistoryItem(history, block, msgId, flags, date, from) } void HistoryMessage::initTime() { - _timeText = date.toString(cTimeFormat()); + _timeText = date.toString(cTimeFormat()) + qsl(" (%1)").arg(id); _timeWidth = st::msgDateFont->m.width(_timeText); _viewsText = (_views >= 0) ? QString::number(_views ? _views : 1) : QString(); @@ -5513,7 +5972,11 @@ void HistoryMessage::draw(Painter &p, uint32 selection) const { if (displayFromName()) { p.setFont(st::msgNameFont->f); - p.setPen(_from->color->p); + if (fromChannel()) { + p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor); + } else { + p.setPen(_from->color); + } _from->nameText.drawElided(p, r.left() + st::msgPadding.left(), r.top() + st::msgPadding.top(), width - st::msgPadding.left() - st::msgPadding.right()); r.setTop(r.top() + st::msgNameFont->height); } @@ -6386,11 +6849,6 @@ void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) { text = lng_action_created_channel(lt_title, textClean(qs(d.vtitle))); } break; - case mtpc_messageActionChannelToggleComments: { - const MTPDmessageActionChannelToggleComments &d(action.c_messageActionChannelToggleComments()); - text = lang(d.venabled.v ? lng_action_comments_enabled : lng_action_comments_disabled); - } break; - case mtpc_messageActionChatDeletePhoto: { text = fromChannel() ? lang(lng_action_removed_photo_channel) : lng_action_removed_photo(lt_from, from); } break; @@ -6468,6 +6926,11 @@ QString HistoryServiceMsg::inReplyText() const { return result.trimmed().startsWith(from()->name) ? result.trimmed().mid(from()->name.size()).trimmed() : result; } +void HistoryServiceMsg::setText(const QString &text) { + _text.setText(st::msgServiceFont, text, _historySrvOptions); + initDimensions(); +} + void HistoryServiceMsg::draw(Painter &p, uint32 selection) const { uint64 ms = App::main() ? App::main()->animActiveTime(id) : 0; if (ms) { @@ -6603,13 +7066,91 @@ HistoryServiceMsg::~HistoryServiceMsg() { delete _media; } -HistoryDateMsg::HistoryDateMsg(History *history, HistoryBlock *block, const QDate &date) : HistoryServiceMsg(history, block, clientMsgId(), QDateTime(date), langDayOfMonth(date)) { +HistoryDateMsg::HistoryDateMsg(History *history, HistoryBlock *block, const QDate &date) : +HistoryServiceMsg(history, block, clientMsgId(), QDateTime(date), langDayOfMonth(date)) { } HistoryItem *createDayServiceMsg(History *history, HistoryBlock *block, QDateTime date) { return regItem(new HistoryDateMsg(history, block, date.date())); } +HistoryGroup::HistoryGroup(History *history, HistoryBlock *block, const MTPDmessageGroup &group, const QDateTime &date) : +HistoryServiceMsg(history, block, clientMsgId(), date, lng_channel_comments_count(lt_count, group.vcount.v) + qsl(" (%1 .. %2)").arg(group.vmin_id.v).arg(group.vmax_id.v)), +_minId(group.vmin_id.v), _maxId(group.vmax_id.v), _count(group.vcount.v), _lnk(new CommentsLink(this)) { +} + +void HistoryGroup::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const { + lnk = TextLinkPtr(); + state = HistoryDefaultCursorState; + + int32 left = st::msgServiceMargin.left(), width = _history->width - st::msgServiceMargin.left() - st::msgServiceMargin.left(), height = _height - st::msgServiceMargin.top() - st::msgServiceMargin.bottom(); // two small margins + if (width < 1) return; + + QRect trect(QRect(left, st::msgServiceMargin.top(), width, height).marginsAdded(-st::msgServicePadding)); + if (width > _maxw) { + left += (width - _maxw) / 2; + width = _maxw; + } + if (QRect(left, st::msgServiceMargin.top(), width, height).contains(x, y)) { + lnk = _lnk; + } +} + +void HistoryGroup::uniteWith(MsgId minId, MsgId maxId, int32 count) { + if (minId == _minId && maxId == _maxId && count == _count) return; + + if (minId < _minId) { + if (maxId <= _minId) { + _count += count; + } else if (maxId <= _maxId) { // :( smth not precise + _count += qMax(0, count - (maxId - _minId)); + } else { // :( smth not precise + _count = qMax(count, _count); + _maxId = maxId; + } + _minId = minId; + } else if (maxId > _maxId) { + if (minId >= _maxId) { + _count += count; + } else if (minId >= _minId) { // :( smth not precise + _count += qMax(0, count - (_maxId - minId)); + } else { // :( smth not precise + _count = qMax(count, _count); + _minId = minId; + } + _maxId = maxId; + } else if (count > _count) { // :( smth not precise + _count = count; + } + updateText(); +} + +bool HistoryGroup::decrementCount() { + if (_count > 1) { + --_count; + updateText(); + return true; + } + return false; +} + +void HistoryGroup::updateText() { + setText(lng_channel_comments_count(lt_count, _count) + qsl(" (%1 .. %2)").arg(_minId).arg(_maxId)); +} + +HistoryCollapse::HistoryCollapse(History *history, HistoryBlock *block, MsgId wasMinId, const QDateTime &date) : +HistoryServiceMsg(history, block, clientMsgId(), date, qsl("-")), +_wasMinId(wasMinId) { +} + +void HistoryCollapse::draw(Painter &p, uint32 selection) const { +} + +void HistoryCollapse::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const { + lnk = TextLinkPtr(); + state = HistoryDefaultCursorState; +} + HistoryUnreadBar::HistoryUnreadBar(History *history, HistoryBlock *block, int32 count, const QDateTime &date) : HistoryItem(history, block, clientMsgId(), 0, date, 0), freezed(false) { setCount(count); initDimensions(); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 962e6c17c..478ee9658 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -34,9 +34,11 @@ extern TextParseOptions _textNameOptions, _textDlgOptions, _historyTextOptions, #include "structs.h" -struct History; -struct Histories : public QHash, public Animated { - typedef QHash Parent; +class History; +class Histories : public Animated { +public: + typedef QHash Map; + Map map; Histories() : unreadFull(0), unreadMuted(0) { } @@ -44,16 +46,17 @@ struct Histories : public QHash, public Animated { void regSendAction(History *history, UserData *user, const MTPSendMessageAction &action); bool animStep(float64 ms); + History *find(const PeerId &peerId); + History *findOrInsert(const PeerId &peerId, int32 unreadCount, int32 maxInboxRead); + void clear(); - Parent::iterator erase(Parent::iterator i); void remove(const PeerId &peer); ~Histories() { clear(); - unreadFull = unreadMuted = 0; } - HistoryItem *addToBack(const MTPmessage &msg, int msgState = 1); // 2 - new read message, 1 - new unread message, 0 - not new message, -1 - searched message + HistoryItem *addNewMessage(const MTPmessage &msg, int msgState = 1); // 2 - new read message, 1 - new unread message, 0 - not new message, -1 - searched message // HistoryItem *addToBack(const MTPgeoChatMessage &msg, bool newMsg = true); typedef QMap TypingHistories; // when typing in this history started @@ -62,7 +65,7 @@ struct Histories : public QHash, public Animated { int32 unreadFull, unreadMuted; }; -struct HistoryBlock; +class HistoryBlock; struct DialogRow { DialogRow(History *history = 0, DialogRow *prev = 0, DialogRow *next = 0, int32 pos = 0) : prev(prev), next(next), history(history), pos(pos), attached(0) { @@ -158,19 +161,29 @@ struct SendAction { class HistoryMedia; class HistoryMessage; class HistoryUnreadBar; -struct History : public QList { + +class ChannelHistory; +class History { +public: + History(const PeerId &peerId); ChannelId channelId() const { return peerToChannel(peer->id); } + bool isChannel() const { + return peerIsChannel(peer->id); + } + ChannelHistory *asChannelHistory(); + const ChannelHistory *asChannelHistory() const; - typedef QList Parent; + bool isEmpty() const { + return blocks.isEmpty(); + } void clear(bool leaveItems = false); - Parent::iterator erase(Parent::iterator i); void blockResized(HistoryBlock *block, int32 dh); void removeBlock(HistoryBlock *block); - ~History() { + virtual ~History() { clear(); } @@ -178,16 +191,14 @@ struct History : public QList { HistoryItem *createItemForwarded(HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg); HistoryItem *createItemDocument(HistoryBlock *block, MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc); - HistoryItem *addToBackService(MsgId msgId, QDateTime date, const QString &text, int32 flags = 0, HistoryMedia *media = 0, bool newMsg = true); - HistoryItem *addToBack(const MTPmessage &msg, bool newMsg = true); + HistoryItem *addNewService(MsgId msgId, QDateTime date, const QString &text, int32 flags = 0, HistoryMedia *media = 0, bool newMsg = true); + HistoryItem *addNewMessage(const MTPmessage &msg, bool newMsg = true); HistoryItem *addToHistory(const MTPmessage &msg); - HistoryItem *addToBackForwarded(MsgId id, QDateTime date, int32 from, HistoryMessage *item); - HistoryItem *addToBackDocument(MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc); + HistoryItem *addNewForwarded(MsgId id, QDateTime date, int32 from, HistoryMessage *item); + HistoryItem *addNewDocument(MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc); - void addToFront(const QVector &slice); - void addToBack(const QVector &slice); - void createInitialDateBlock(const QDateTime &date); - HistoryItem *doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem *adding, bool newMsg); + void addOlderSlice(const QVector &slice, const QVector *collapsed); + void addNewerSlice(const QVector &slice, const QVector *collapsed); void addToOverview(HistoryItem *item, MediaOverviewType type); bool addToOverviewFront(HistoryItem *item, MediaOverviewType type); @@ -195,6 +206,7 @@ struct History : public QList { void unregTyping(UserData *from); int32 countUnread(MsgId upTo); + void updateShowFrom(); MsgId inboxRead(MsgId upTo); MsgId inboxRead(HistoryItem *wasRead); MsgId outboxRead(MsgId upTo); @@ -210,8 +222,8 @@ struct History : public QList { bool loadedAtBottom() const; // last message is in the list void setNotLoadedAtBottom(); bool loadedAtTop() const; // nothing was added after loading history back - bool isReadyFor(MsgId msgId, bool check = false) const; // has messages for showing history at msgId - void getReadyFor(MsgId msgId); + bool isReadyFor(MsgId msgId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop); // has messages for showing history at msgId + void getReadyFor(MsgId msgId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop); void setLastMessage(HistoryItem *msg, bool updatePosInDialogs = true); void setPosInDialogsDate(const QDateTime &date); @@ -222,18 +234,6 @@ struct History : public QList { MsgId msgIdForRead() const; int32 geomResize(int32 newWidth, int32 *ytransform = 0, HistoryItem *resizedItem = 0); // return new size - int32 width, height, msgCount, unreadCount; - int32 inboxReadBefore, outboxReadBefore; - HistoryItem *showFrom; - HistoryUnreadBar *unreadBar; - - PeerData *peer; - bool oldLoaded, newLoaded; - HistoryItem *lastMsg; - QDateTime lastMsgDate; - - typedef QList NotifyQueue; - NotifyQueue notifies; void removeNotification(HistoryItem *item) { if (!notifies.isEmpty()) { @@ -275,6 +275,27 @@ struct History : public QList { // showFrom can't be detached } + void paintDialog(Painter &p, int32 w, bool sel) const; + void eraseFromOverview(MediaOverviewType type, MsgId msgId); + bool updateTyping(uint64 ms = 0, uint32 dots = 0, bool force = false); + void clearLastKeyboard(); + + typedef QList Blocks; + Blocks blocks; + + int32 width, height, msgCount, unreadCount; + int32 inboxReadBefore, outboxReadBefore; + HistoryItem *showFrom; + HistoryUnreadBar *unreadBar; + + PeerData *peer; + bool oldLoaded, newLoaded; + HistoryItem *lastMsg; + QDateTime lastMsgDate; + + typedef QList NotifyQueue; + NotifyQueue notifies; + QString draft; MsgId draftToId; MessageCursor draftCursor; @@ -286,15 +307,12 @@ struct History : public QList { bool lastKeyboardInited, lastKeyboardUsed; MsgId lastKeyboardId; PeerId lastKeyboardFrom; - void clearLastKeyboard(); mtpRequestId sendRequestId; mutable const HistoryItem *textCachedFor; // cache mutable Text lastItemTextCache; - void paintDialog(Painter &p, int32 w, bool sel) const; - typedef QMap DialogLinks; DialogLinks dialogs; uint64 posInDialogs; // like ((unixtime) << 32) | (incremented counter) @@ -306,19 +324,71 @@ struct History : public QList { QString typingStr; Text typingText; uint32 typingFrame; - bool updateTyping(uint64 ms = 0, uint32 dots = 0, bool force = false); QMap mySendActions; typedef QList MediaOverview; typedef QMap MediaOverviewIds; + MediaOverview overview[OverviewCount]; + MediaOverviewIds overviewIds[OverviewCount]; + int32 overviewCount[OverviewCount]; // -1 - not loaded, 0 - all loaded, > 0 - count, but not all loaded - MediaOverview _overview[OverviewCount]; - MediaOverviewIds _overviewIds[OverviewCount]; - int32 _overviewCount[OverviewCount]; // -1 - not loaded, 0 - all loaded, > 0 - count, but not all loaded +private: - void eraseFromOverview(MediaOverviewType type, MsgId msgId); + HistoryItem *addMessageGroupAfterPrevToBlock(const MTPDmessageGroup &group, const QDateTime &date, HistoryItem *prev, HistoryBlock *block); + HistoryItem *addNewItem(HistoryBlock *to, bool newBlock, HistoryItem *adding, bool newMsg); + +protected: + + void createInitialDateBlock(const QDateTime &date); + HistoryItem *addItemAfterPrevToBlock(HistoryItem *item, HistoryItem *prev, HistoryBlock *block); + HistoryItem *addNewInTheMiddle(HistoryItem *newItem, int32 blockIndex, int32 itemIndex); + + friend class HistoryBlock; + +}; + +class HistoryGroup; +class HistoryCollapse; +class ChannelHistory : public History { +public: + + ChannelHistory(const PeerId &peer); + + void messageDetached(HistoryItem *msg); + void messageDeleted(HistoryItem *msg); + void messageWithIdDeleted(MsgId msgId); + + bool isSwitchReadyFor(MsgId switchId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop); // has messages for showing history after switching mode at switchId + void getSwitchReadyFor(MsgId switchId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop); + + void insertCollapseItem(MsgId wasMinId); + + int32 unreadCountAll; + bool onlyImportant() const { + return _onlyImportant; + } + + HistoryCollapse *collapse() const { + return _collapse; + } + +private: + + HistoryGroup *findGroup(MsgId msgId) const; + HistoryBlock *findGroupBlock(MsgId msgId) const; + HistoryGroup *findGroupInOther(MsgId msgId) const; + HistoryItem *findPrevItem(HistoryItem *item) const; + bool _onlyImportant; + + typedef QList OtherList; + OtherList _otherList; + bool _otherOldLoaded, _otherNewLoaded; + int32 _otherMsgCount; + + HistoryCollapse *_collapse; + + void switchMode(); - static const int32 ScrollMax = INT_MAX; }; enum DialogsSortMode { @@ -596,13 +666,15 @@ struct DialogsIndexed { DialogsIndex index; }; -struct HistoryBlock : public QVector { +class HistoryBlock { +public: HistoryBlock(History *hist) : y(0), height(0), history(hist) { } - typedef QVector Parent; + typedef QVector Items; + Items items; + void clear(bool leaveItems = false); - Parent::iterator erase(Parent::iterator i); ~HistoryBlock() { clear(); } @@ -667,18 +739,25 @@ enum InfoDisplayType { InfoDisplayOverImage, }; +inline bool isImportantChannelMessage(int32 flags) { + /*(flags & MTPDmessage_flag_out) || (flags & MTPDmessage_flag_notify_by_from) || */ + return !(flags & MTPDmessage::flag_from_id) && (flags != 0); // always has_from_id || has_views +} + +enum HistoryItemType { + HistoryItemMsg = 0, + HistoryItemDate, + HistoryItemUnreadBar, + HistoryItemGroup, + HistoryItemCollapse +}; + class HistoryMedia; class HistoryItem : public HistoryElem { public: HistoryItem(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime msgDate, int32 from); - enum { - MsgType = 0, - DateType, - UnreadBarType - }; - virtual void initDimensions() = 0; virtual int32 resize(int32 width) = 0; // return new height virtual void draw(Painter &p, uint32 selection) const = 0; @@ -732,6 +811,9 @@ public: bool fromChannel() const { return _from->isChannel(); } + bool isImportant() const { + return _history->isChannel() && isImportantChannelMessage(_flags); + } virtual bool needCheck() const { return out(); } @@ -750,8 +832,8 @@ public: virtual uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const { return (from << 16) | to; } - virtual int32 itemType() const { - return MsgType; + virtual HistoryItemType type() const { + return HistoryItemMsg; } virtual bool serviceMsg() const { return false; @@ -896,6 +978,18 @@ private: MsgId _msgid; }; +class CommentsLink : public ITextLink { + TEXT_LINK_CLASS(CommentsLink) + +public: + CommentsLink(HistoryItem *item) : _item(item) { + } + void onClick(Qt::MouseButton button) const; + +private: + HistoryItem *_item; +}; + HistoryItem *regItem(HistoryItem *item, bool returnExisting = false); class HistoryMedia : public HistoryElem { @@ -1596,6 +1690,8 @@ public: return _media ? _media->animating() : false; } + void setText(const QString &text); + ~HistoryServiceMsg(); protected: @@ -1624,11 +1720,77 @@ public: QString selectedText(uint32 selection) const { return QString(); } - int32 itemType() const { - return DateType; + HistoryItemType type() const { + return HistoryItemDate; } }; +class HistoryGroup : public HistoryServiceMsg { +public: + + HistoryGroup(History *history, HistoryBlock *block, const MTPDmessageGroup &group, const QDateTime &date); + void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; + void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const { + symbol = 0xFFFF; + after = false; + upon = false; + } + QString selectedText(uint32 selection) const { + return QString(); + } + HistoryItemType type() const { + return HistoryItemGroup; + } + void uniteWith(MsgId minId, MsgId maxId, int32 count); + void uniteWith(const HistoryGroup *other) { + uniteWith(other->_minId, other->_maxId, other->_count); + } + + bool decrementCount(); // returns true if result count > 0 + + MsgId minId() const { + return _minId; + } + MsgId maxId() const { + return _maxId; + } + +private: + MsgId _minId, _maxId; + int32 _count; + + TextLinkPtr _lnk; + + void updateText(); + +}; + +class HistoryCollapse : public HistoryServiceMsg { +public: + + HistoryCollapse(History *history, HistoryBlock *block, MsgId wasMinId, const QDateTime &date); + void draw(Painter &p, uint32 selection) const; + void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; + void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const { + symbol = 0xFFFF; + after = false; + upon = false; + } + QString selectedText(uint32 selection) const { + return QString(); + } + HistoryItemType type() const { + return HistoryItemCollapse; + } + MsgId wasMinId() const { + return _wasMinId; + } + +private: + MsgId _wasMinId; + +}; + HistoryItem *createDayServiceMsg(History *history, HistoryBlock *block, QDateTime date); class HistoryUnreadBar : public HistoryItem { @@ -1649,8 +1811,8 @@ public: QString selectedText(uint32 selection) const { return QString(); } - int32 itemType() const { - return UnreadBarType; + HistoryItemType type() const { + return HistoryItemUnreadBar; } protected: diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 2d510b831..346a4b536 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -82,12 +82,12 @@ HistoryList::HistoryList(HistoryWidget *historyWidget, ScrollArea *scroll, Histo setMouseTracking(true); } -void HistoryList::messagesReceived(const QVector &messages) { - hist->addToFront(messages); +void HistoryList::messagesReceived(const QVector &messages, const QVector *collapsed) { + hist->addOlderSlice(messages, collapsed); } -void HistoryList::messagesReceivedDown(const QVector &messages) { - hist->addToBack(messages); +void HistoryList::messagesReceivedDown(const QVector &messages, const QVector *collapsed) { + hist->addNewerSlice(messages, collapsed); } void HistoryList::updateMsg(const HistoryItem *msg) { @@ -123,8 +123,8 @@ void HistoryList::paintEvent(QPaintEvent *e) { } if (!_firstLoading && !hist->isEmpty()) { adjustCurrent(r.top()); - HistoryBlock *block = (*hist)[currentBlock]; - HistoryItem *item = (*block)[currentItem]; + HistoryBlock *block = hist->blocks[currentBlock]; + HistoryItem *item = block->items[currentItem]; SelectedItems::const_iterator selEnd = _selected.cend(); bool hasSel = !_selected.isEmpty(); @@ -153,15 +153,15 @@ void HistoryList::paintEvent(QPaintEvent *e) { item->draw(p, sel); p.translate(0, h); ++iItem; - if (iItem == block->size()) { + if (iItem == block->items.size()) { iItem = 0; ++iBlock; - if (iBlock == hist->size()) { + if (iBlock == hist->blocks.size()) { break; } - block = (*hist)[iBlock]; + block = hist->blocks[iBlock]; } - item = (*block)[iItem]; + item = block->items[iItem]; y += h; } } @@ -685,7 +685,7 @@ void HistoryList::dragActionFinish(const QPoint &screenPos, Qt::MouseButton butt } } else { _selected.clear(); - parentWidget()->update(); + update(); } } else if (_dragAction == Selecting) { if (_dragSelFrom && _dragSelTo) { @@ -818,18 +818,18 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (dynamic_cast(App::hoveredLinkItem()) && App::hoveredLinkItem()->id > 0) { _menu->addAction(lang(lng_context_forward_msg), historyWidget, SLOT(forwardMessage()))->setEnabled(true); } - if ((!hist->peer->isChannel() || hist->peer->asChannel()->adminned)) { + if ((!hist->peer->isChannel() || hist->peer->asChannel()->adminned || App::hoveredLinkItem()->out())) { _menu->addAction(lang(lng_context_delete_msg), historyWidget, SLOT(deleteMessage()))->setEnabled(true); } } - if (App::hoveredLinkItem()->id > 0 && (!hist->peer->isChannel() || hist->peer->asChannel()->adminned)) { + if (App::hoveredLinkItem()->id > 0 && !App::hoveredLinkItem()->serviceMsg() && (!hist->peer->isChannel() || hist->peer->asChannel()->adminned)) { _menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true); } App::contextItem(App::hoveredLinkItem()); } } else { // maybe cursor on some text history item? - bool canDelete = (item && item->itemType() == HistoryItem::MsgType) && (!hist->peer->isChannel() || hist->peer->asChannel()->adminned); - bool canForward = (item && item->itemType() == HistoryItem::MsgType) && (item->id > 0) && !item->serviceMsg(); + bool canDelete = (item && item->type() == HistoryItemMsg) && (!hist->peer->isChannel() || hist->peer->asChannel()->adminned || item->out()); + bool canForward = (item && item->type() == HistoryItemMsg) && (item->id > 0) && !item->serviceMsg(); HistoryMessage *msg = dynamic_cast(item); HistoryServiceMsg *srv = dynamic_cast(item); @@ -897,11 +897,11 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu->addAction(lang((msg && msg->uploading()) ? lng_context_cancel_upload : lng_context_delete_msg), historyWidget, SLOT(deleteMessage()))->setEnabled(true); } } - if (item->id > 0 && (!hist->peer->isChannel() || hist->peer->asChannel()->adminned)) { + if (item->id > 0 && !item->serviceMsg() && (!hist->peer->isChannel() || hist->peer->asChannel()->adminned)) { _menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true); } } else { - if (App::mousedItem() && App::mousedItem()->itemType() == HistoryItem::MsgType && App::mousedItem()->id > 0 && (!hist->peer->isChannel() || hist->peer->asChannel()->adminned)) { + if (App::mousedItem() && !App::mousedItem()->serviceMsg() && App::mousedItem()->id > 0 && (!hist->peer->isChannel() || hist->peer->asChannel()->adminned)) { if (!_menu) _menu = new ContextMenu(this); _menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true); item = App::mousedItem(); @@ -1006,7 +1006,7 @@ void HistoryList::saveContextFile() { void HistoryList::copyContextText() { HistoryItem *item = App::contextItem(); - if (item && item->itemType() != HistoryItem::MsgType) { + if (item && item->type() != HistoryItemMsg) { item = 0; } @@ -1158,6 +1158,38 @@ void HistoryList::setFirstLoading(bool loading) { update(); } +HistoryItem *HistoryList::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;) { + --blockIndex; + HistoryBlock *block = hist->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); + return item; + } + } + itemIndex = 0; + } + for (int32 blockIndex = currentBlock, itemIndex = currentItem + 1; blockIndex < hist->blocks.size(); ++blockIndex) { + HistoryBlock *block = hist->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); + return item; + } + } + itemIndex = 0; + } + return 0; +} + void HistoryList::updateSize() { int32 ph = scrollArea->height(), minadd = 0; int32 newYSkip = ph - (hist->height + st::historyPadding); @@ -1211,30 +1243,30 @@ HistoryList::~HistoryList() { _dragAction = NoDrag; } -void HistoryList::adjustCurrent(int32 y) { +void HistoryList::adjustCurrent(int32 y) const { if (hist->isEmpty()) return; - if (currentBlock >= hist->size()) { - currentBlock = hist->size() - 1; + if (currentBlock >= hist->blocks.size()) { + currentBlock = hist->blocks.size() - 1; currentItem = 0; } - while ((*hist)[currentBlock]->y + ySkip > y && currentBlock > 0) { + while (hist->blocks[currentBlock]->y + ySkip > y && currentBlock > 0) { --currentBlock; currentItem = 0; } - while ((*hist)[currentBlock]->y + (*hist)[currentBlock]->height + ySkip <= y && currentBlock + 1 < hist->size()) { + while (hist->blocks[currentBlock]->y + hist->blocks[currentBlock]->height + ySkip <= y && currentBlock + 1 < hist->blocks.size()) { ++currentBlock; currentItem = 0; } - HistoryBlock *block = (*hist)[currentBlock]; - if (currentItem >= block->size()) { - currentItem = block->size() - 1; + HistoryBlock *block = hist->blocks[currentBlock]; + if (currentItem >= block->items.size()) { + currentItem = block->items.size() - 1; } int32 by = block->y; - while ((*block)[currentItem]->y + by + ySkip > y && currentItem > 0) { + while (block->items[currentItem]->y + by + ySkip > y && currentItem > 0) { --currentItem; } - while ((*block)[currentItem]->y + (*block)[currentItem]->height() + by + ySkip <= y && currentItem + 1 < block->size()) { + while (block->items[currentItem]->y + block->items[currentItem]->height() + by + ySkip <= y && currentItem + 1 < block->items.size()) { ++currentItem; } } @@ -1242,13 +1274,13 @@ void HistoryList::adjustCurrent(int32 y) { HistoryItem *HistoryList::prevItem(HistoryItem *item) { if (!item) return 0; HistoryBlock *block = item->block(); - int32 blockIndex = hist->indexOf(block), itemIndex = block->indexOf(item); + int32 blockIndex = hist->blocks.indexOf(block), itemIndex = block->items.indexOf(item); if (blockIndex < 0 || itemIndex < 0) return 0; if (itemIndex > 0) { - return (*block)[itemIndex - 1]; + return block->items[itemIndex - 1]; } if (blockIndex > 0) { - return *((*hist)[blockIndex - 1]->cend() - 1); + return hist->blocks[blockIndex - 1]->items.back(); } return 0; } @@ -1256,13 +1288,13 @@ HistoryItem *HistoryList::prevItem(HistoryItem *item) { HistoryItem *HistoryList::nextItem(HistoryItem *item) { if (!item) return 0; HistoryBlock *block = item->block(); - int32 blockIndex = hist->indexOf(block), itemIndex = block->indexOf(item); + int32 blockIndex = hist->blocks.indexOf(block), itemIndex = block->items.indexOf(item); if (blockIndex < 0 || itemIndex < 0) return 0; - if (itemIndex + 1 < block->size()) { - return (*block)[itemIndex + 1]; + if (itemIndex + 1 < block->items.size()) { + return block->items[itemIndex + 1]; } - if (blockIndex + 1 < hist->size()) { - return *(*hist)[blockIndex + 1]->cbegin(); + if (blockIndex + 1 < hist->blocks.size()) { + return hist->blocks[blockIndex + 1]->items.front(); } return 0; } @@ -1278,7 +1310,7 @@ bool HistoryList::canDeleteSelected() const { void HistoryList::getSelectionState(int32 &selectedForForward, int32 &selectedForDelete) const { selectedForForward = selectedForDelete = 0; for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { - if (i.key()->itemType() == HistoryItem::MsgType && i.value() == FullItemSel) { + if (i.key()->type() == HistoryItemMsg && i.value() == FullItemSel) { ++selectedForDelete; if (!i.key()->serviceMsg() && i.key()->id > 0) { ++selectedForForward; @@ -1337,8 +1369,8 @@ void HistoryList::onUpdateSelected() { if (!hist->isEmpty()) { adjustCurrent(point.y()); - block = (*hist)[currentBlock]; - item = (*block)[currentItem]; + block = hist->blocks[currentBlock]; + item = block->items[currentItem]; App::mousedItem(item); m = mapMouseToItem(point, item); @@ -1505,7 +1537,7 @@ void HistoryList::updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dra } if (!force) return; - parentWidget()->update(); + update(); } void HistoryList::applyDragSelection() { @@ -1519,13 +1551,13 @@ void HistoryList::applyDragSelection(SelectedItems *toItems) const { int32 fromy = _dragSelFrom->y + _dragSelFrom->block()->y, toy = _dragSelTo->y + _dragSelTo->block()->y + _dragSelTo->height(); if (_dragSelecting) { - int32 fromblock = hist->indexOf(_dragSelFrom->block()), fromitem = _dragSelFrom->block()->indexOf(_dragSelFrom); - int32 toblock = hist->indexOf(_dragSelTo->block()), toitem = _dragSelTo->block()->indexOf(_dragSelTo); + 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)[fromblock]; - for (int32 cnt = (fromblock < toblock) ? block->size() : (toitem + 1); fromitem < cnt; ++fromitem) { - HistoryItem *item = (*block)[fromitem]; + 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()) { @@ -2066,7 +2098,7 @@ bool HistoryHider::withConfirm() const { } void HistoryHider::paintEvent(QPaintEvent *e) { - QPainter p(this); + Painter p(this); if (!hiding || !cacheForAnim.isNull() || !offered) { p.setOpacity(aOpacity.current() * st::layerAlpha); p.fillRect(0, st::titleShadow, width(), height() - st::titleShadow, st::layerBG->b); @@ -2234,6 +2266,15 @@ HistoryHider::~HistoryHider() { parent()->noHider(this); } +CollapseButton::CollapseButton(QWidget *parent) : FlatButton(parent, lang(lng_channel_hide_comments), st::collapseButton) { +} + +void CollapseButton::paintEvent(QPaintEvent *e) { + Painter p(this); + App::roundRect(p, rect(), App::msgServiceBg(), ServiceCorners); + FlatButton::paintEvent(e); +} + HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _replyToId(0) , _replyTo(0) @@ -2250,6 +2291,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _channel(NoChannel) , _clearPeer(0) , _showAtMsgId(0) +, _fixedInScrollMsgId(0) +, _fixedInScrollMsgTop(0) , _preloadRequest(0), _preloadDownRequest(0) , _delayedShowAtMsgId(-1) , _delayedShowAtRequest(0) @@ -2259,6 +2302,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _history(0) , _histInited(false) , _toHistoryEnd(this, st::historyToEnd) +, _collapseComments(this) , _attachMention(this) , _reportSpamPanel(this) , _send(this, lang(lng_send_button), st::btnSend) @@ -2309,6 +2353,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(&_reportSpamPanel, SIGNAL(hideClicked()), this, SLOT(onReportSpamHide())); connect(&_reportSpamPanel, SIGNAL(clearClicked()), this, SLOT(onReportSpamClear())); connect(&_toHistoryEnd, SIGNAL(clicked()), this, SLOT(onHistoryToEnd())); + connect(&_collapseComments, SIGNAL(clicked()), this, SLOT(onCollapseComments())); connect(&_replyForwardPreviewCancel, SIGNAL(clicked()), this, SLOT(onReplyForwardPreviewCancel())); connect(&_send, SIGNAL(clicked()), this, SLOT(onSend())); connect(&_unblock, SIGNAL(clicked()), this, SLOT(onUnblock())); @@ -2354,6 +2399,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _scroll.hide(); _scroll.move(0, 0); + _collapseComments.setParent(&_scroll); _kbScroll.setFocusPolicy(Qt::NoFocus); _kbScroll.viewport()->setFocusPolicy(Qt::NoFocus); @@ -2367,6 +2413,9 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _toHistoryEnd.hide(); _toHistoryEnd.installEventFilter(this); + _collapseComments.hide(); + _collapseComments.installEventFilter(this); + _attachMention.hide(); connect(&_attachMention, SIGNAL(chosen(QString)), this, SLOT(onMentionHashtagOrBotCommandInsert(QString))); _field.installEventFilter(&_attachMention); @@ -2759,7 +2808,7 @@ void HistoryWidget::setKbWasHidden() { } void HistoryWidget::fastShowAtEnd(History *h) { - h->getReadyFor(ShowAtTheEndMsgId); + h->getReadyFor(ShowAtTheEndMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); if (_history != h) return; @@ -2768,7 +2817,7 @@ void HistoryWidget::fastShowAtEnd(History *h) { _showAtMsgId = ShowAtTheEndMsgId; _histInited = false; - if (h->isReadyFor(_showAtMsgId)) { + if (h->isReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop)) { historyLoaded(); } else { firstLoadMessages(); @@ -2781,10 +2830,19 @@ void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) { if (_peer->id == peerId) { _history->lastWidth = 0; - bool canShowNow = _history->isReadyFor(showAtMsgId, true); + bool wasOnlyImportant = _history->isChannel() ? _history->asChannelHistory()->onlyImportant() : true; + + bool canShowNow = _history->isReadyFor(showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); + if (_fixedInScrollMsgId) { + _fixedInScrollMsgTop += _list->height() - _scroll.scrollTop() - st::historyPadding; + } if (!canShowNow) { delayedShowAt(showAtMsgId); } else { + if (_history->isChannel() && wasOnlyImportant != _history->asChannelHistory()->onlyImportant()) { + clearAllLoadRequests(); + } + clearDelayedShowAt(); if (_replyReturn && _replyReturn->id == showAtMsgId) { calcNextReplyReturn(); @@ -2896,7 +2954,9 @@ void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) { _scroll.setWidget(_list); _list->show(); - if (_history->lastWidth || _history->isReadyFor(_showAtMsgId, true)) { + if (_history->lastWidth || _history->isReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop)) { + _fixedInScrollMsgId = 0; + _fixedInScrollMsgTop = 0; historyLoaded(); } else { firstLoadMessages(); @@ -2993,17 +3053,17 @@ void HistoryWidget::updateReportSpamStatus() { return; } } - if ((!_history->loadedAtTop() && (_history->size() < 2 || (_history->size() == 2 && _history->at(1)->size() < 2))) || !cContactsReceived() || _firstLoadRequest) { + if ((!_history->loadedAtTop() && (_history->blocks.size() < 2 || (_history->blocks.size() == 2 && _history->blocks.at(1)->items.size() < 2))) || !cContactsReceived() || _firstLoadRequest) { _reportSpamStatus = dbiprsUnknown; } else if (_peer->isUser()) { if (_peer->asUser()->contact > 0) { _reportSpamStatus = dbiprsNoButton; } else { bool anyFound = false, outFound = false; - for (int32 i = 0, l = _history->size(); i < l; ++i) { - for (int32 j = 0, c = _history->at(i)->size(); j < c; ++j) { + for (int32 i = 0, l = _history->blocks.size(); i < l; ++i) { + for (int32 j = 0, c = _history->blocks.at(i)->items.size(); j < c; ++j) { anyFound = true; - if (_history->at(i)->at(j)->out()) { + if (_history->blocks.at(i)->items.at(j)->out()) { outFound = true; break; } @@ -3055,6 +3115,7 @@ void HistoryWidget::updateControlsVisibility() { _attachPhoto.hide(); _attachEmoji.hide(); _toHistoryEnd.hide(); + _collapseComments.hide(); _kbShow.hide(); _kbHide.hide(); _cmdStart.hide(); @@ -3233,7 +3294,7 @@ void HistoryWidget::newUnreadMsg(History *history, HistoryItem *item) { } void HistoryWidget::historyToDown(History *history) { - history->lastScrollTop = History::ScrollMax; + history->lastScrollTop = ScrollMax; if (history == _history) { _scroll.scrollToY(_scroll.scrollTopMax()); } @@ -3274,38 +3335,39 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages int32 count = 0; const QVector emptyList, *histList = &emptyList; + const QVector *histCollapsed = 0; switch (messages.type()) { case mtpc_messages_messages: { - const MTPDmessages_messages &data(messages.c_messages_messages()); - App::feedUsers(data.vusers); - App::feedChats(data.vchats); - histList = &data.vmessages.c_vector().v; + const MTPDmessages_messages &d(messages.c_messages_messages()); + App::feedUsers(d.vusers); + App::feedChats(d.vchats); + histList = &d.vmessages.c_vector().v; count = histList->size(); } break; case mtpc_messages_messagesSlice: { - const MTPDmessages_messagesSlice &data(messages.c_messages_messagesSlice()); - App::feedUsers(data.vusers); - App::feedChats(data.vchats); - histList = &data.vmessages.c_vector().v; - count = data.vcount.v; + const MTPDmessages_messagesSlice &d(messages.c_messages_messagesSlice()); + App::feedUsers(d.vusers); + App::feedChats(d.vchats); + histList = &d.vmessages.c_vector().v; + count = d.vcount.v; } break; case mtpc_messages_channelMessages: { - const MTPDmessages_channelMessages &data(messages.c_messages_channelMessages()); + const MTPDmessages_channelMessages &d(messages.c_messages_channelMessages()); if (peer && peer->isChannel()) { - peer->asChannel()->ptsReceived(data.vpts.v); + peer->asChannel()->ptsReceived(d.vpts.v); } else { - LOG(("App Error: received messages.channelMessages in HistoryWidget::messagesReceived when no channel was passed!")); + LOG(("API Error: received messages.channelMessages when no channel was passed! (HistoryWidget::messagesReceived)")); } - - App::feedUsers(data.vusers); - App::feedChats(data.vchats); - histList = &data.vmessages.c_vector().v; - count = data.vcount.v; + App::feedUsers(d.vusers); + App::feedChats(d.vchats); + histList = &d.vmessages.c_vector().v; + if (d.has_collapsed()) histCollapsed = &d.vcollapsed.c_vector().v; + count = d.vcount.v; } break; } - + if (_preloadRequest == requestId) { - addMessagesToFront(*histList); + addMessagesToFront(*histList, histCollapsed); _preloadRequest = 0; onListScroll(); if (_reportSpamStatus == dbiprsUnknown) { @@ -3313,12 +3375,15 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages if (_reportSpamStatus != dbiprsUnknown) updateControlsVisibility(); } } else if (_preloadDownRequest == requestId) { - addMessagesToBack(*histList); + addMessagesToBack(*histList, histCollapsed); _preloadDownRequest = 0; onListScroll(); if (_history->loadedAtBottom() && App::wnd()) App::wnd()->checkHistoryActivation(); } else if (_firstLoadRequest == requestId) { - addMessagesToFront(*histList); + addMessagesToFront(*histList, histCollapsed); + if (_fixedInScrollMsgId && _history->isChannel()) { + _history->asChannelHistory()->insertCollapseItem(_fixedInScrollMsgId); + } _firstLoadRequest = 0; if (_history->loadedAtTop()) { if (_history->unreadCount > count) { @@ -3333,14 +3398,21 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages historyLoaded(); } else if (_delayedShowAtRequest == requestId) { _delayedShowAtRequest = 0; - _history->getReadyFor(_delayedShowAtMsgId); + bool wasOnlyImportant = _history->isChannel() ? _history->asChannelHistory()->onlyImportant() : true; + _history->getReadyFor(_delayedShowAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); + if (_fixedInScrollMsgId) { + _fixedInScrollMsgTop += _list->height() - _scroll.scrollTop() - st::historyPadding; + } if (_history->isEmpty()) { if (_preloadRequest) MTP::cancel(_preloadRequest); if (_preloadDownRequest) MTP::cancel(_preloadDownRequest); if (_firstLoadRequest) MTP::cancel(_firstLoadRequest); _preloadRequest = _preloadDownRequest = 0; _firstLoadRequest = -1; // hack - don't updateListSize yet - addMessagesToFront(*histList); + addMessagesToFront(*histList, histCollapsed); + if (_fixedInScrollMsgId && _history->isChannel()) { + _history->asChannelHistory()->insertCollapseItem(_fixedInScrollMsgId); + } _firstLoadRequest = 0; if (_history->loadedAtTop()) { if (_history->unreadCount > count) { @@ -3359,6 +3431,10 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages _showAtMsgId = _delayedShowAtMsgId; _histInited = false; + if (_history->isChannel() && wasOnlyImportant != _history->asChannelHistory()->onlyImportant()) { + clearAllLoadRequests(); + } + historyLoaded(); } } @@ -3386,24 +3462,49 @@ bool HistoryWidget::isActive() const { void HistoryWidget::firstLoadMessages() { if (!_history || _firstLoadRequest) return; + bool loadImportant = _history->isChannel() ? _history->asChannelHistory()->onlyImportant() : false, wasOnlyImportant = loadImportant; int32 from = 0, offset = 0, loadCount = MessagesPerPage; if (_showAtMsgId == ShowAtUnreadMsgId) { if (_history->unreadCount) { - _history->getReadyFor(_showAtMsgId); + _history->getReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); offset = -loadCount / 2; from = _history->inboxReadBefore; } else { - _history->getReadyFor(ShowAtTheEndMsgId); + _history->getReadyFor(ShowAtTheEndMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); } } else if (_showAtMsgId == ShowAtTheEndMsgId) { - _history->getReadyFor(_showAtMsgId); + _history->getReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); loadCount = MessagesFirstLoad; } else if (_showAtMsgId > 0) { - _history->getReadyFor(_showAtMsgId); + _history->getReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); offset = -loadCount / 2; from = _showAtMsgId; + } else if (_showAtMsgId < 0 && _history->isChannel()) { + if (_showAtMsgId == SwitchAtTopMsgId) { + _history->getReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); + loadImportant = true; + } else if (HistoryItem *item = App::histItemById(_channel, _delayedShowAtMsgId)) { + if (item->type() == HistoryItemGroup) { + _history->getReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop); + offset = -loadCount / 2; + from = 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); + loadImportant = true; + } + } + if (_fixedInScrollMsgId) { + _fixedInScrollMsgTop += _list->height() - _scroll.scrollTop() - st::historyPadding; + } + if (_history->isEmpty() || wasOnlyImportant != loadImportant) { + clearAllLoadRequests(); + } } - if (_peer->isChannel()) { + + 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)); } 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)); @@ -3413,9 +3514,11 @@ void HistoryWidget::firstLoadMessages() { void HistoryWidget::loadMessages() { if (!_history || _history->loadedAtTop() || _preloadRequest) return; + bool loadImportant = _history->isChannel() ? _history->asChannelHistory()->onlyImportant() : false; MsgId min = _history->minMsgId(); int32 offset = 0, loadCount = min ? MessagesPerPage : MessagesFirstLoad; - if (_peer->isChannel()) { + + 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)); } 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)); @@ -3428,8 +3531,10 @@ void HistoryWidget::loadMessagesDown() { MsgId max = _history->maxMsgId(); if (!max) return; + bool loadImportant = _history->isChannel() ? _history->asChannelHistory()->onlyImportant() : false; int32 loadCount = MessagesPerPage, offset = -loadCount; - if (_peer->isChannel()) { + + 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)); } 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)); @@ -3442,22 +3547,37 @@ void HistoryWidget::delayedShowAt(MsgId showAtMsgId) { clearDelayedShowAt(); _delayedShowAtMsgId = showAtMsgId; - int32 from = _delayedShowAtMsgId, offset = 0, loadCount = MessagesPerPage; + bool loadImportant = _history->isChannel() ? _history->asChannelHistory()->onlyImportant() : false; + int32 from = 0, offset = 0, loadCount = MessagesPerPage; if (_delayedShowAtMsgId == ShowAtUnreadMsgId) { if (_history->unreadCount) { offset = -loadCount / 2; from = _history->inboxReadBefore; } else { loadCount = MessagesFirstLoad; - from = 0; } } else if (_delayedShowAtMsgId == ShowAtTheEndMsgId) { loadCount = MessagesFirstLoad; - from = 0; } else if (_delayedShowAtMsgId > 0) { offset = -loadCount / 2; + from = _delayedShowAtMsgId; + } else if (_delayedShowAtMsgId < 0 && _history->isChannel()) { + 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); + loadImportant = false; + } else if (item->type() == HistoryItemCollapse) { + offset = -loadCount / 2; + from = qMax(static_cast(item)->wasMinId(), 1); + loadImportant = true; + } + } } - if (_peer->isChannel()) { + + 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)); } 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)); @@ -3469,6 +3589,7 @@ void HistoryWidget::onListScroll() { if (_firstLoadRequest || _scroll.isHidden()) return; updateToEndVisibility(); + updateCollapseCommentsVisibility(); int st = _scroll.scrollTop(), stm = _scroll.scrollTopMax(), sh = _scroll.height(); if (st + PreloadHeightsCount * sh > stm) { @@ -3480,7 +3601,7 @@ void HistoryWidget::onListScroll() { } while (_replyReturn) { - bool below = (_replyReturn->detached() && !_history->isEmpty() && _replyReturn->id < _history->back()->back()->id); + 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); if (below) { calcNextReplyReturn(); @@ -3513,6 +3634,10 @@ void HistoryWidget::onHistoryToEnd() { } } +void HistoryWidget::onCollapseComments() { + showPeerHistory(_peer->id, SwitchAtTopMsgId); +} + void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { if (!_history) return; @@ -3638,7 +3763,7 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const } else { flags |= MTPDmessage::flag_from_id; } - h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(peer), MTPPeer(), MTPint(), MTP_int(replyToId()), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname), MTP_int(userId)), MTPnullMarkup, MTPnullEntities, MTP_int(1))); + h->addNewMessage(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(peer), MTPPeer(), MTPint(), MTP_int(replyToId()), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname), MTP_int(userId)), MTPnullMarkup, MTPnullEntities, MTP_int(1))); h->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_int(sendFlags), p->input, MTP_int(replyTo), MTP_inputMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, h->sendRequestId); App::historyRegRandom(randomId, newId); @@ -3662,6 +3787,14 @@ MsgId HistoryWidget::msgId() const { return _showAtMsgId; } +HistoryItem *HistoryWidget::atTopImportantMsg(int32 &bottomUnderScrollTop) const { + if (!_list || !_history->isChannel()) { + bottomUnderScrollTop = 0; + return 0; + } + return _list->atTopImportantMsg(_scroll.scrollTop(), _scroll.height(), bottomUnderScrollTop); +} + void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back) { _bgAnimCache = bgAnimCache; _bgAnimTopBarCache = bgAnimTopBarCache; @@ -3673,6 +3806,7 @@ void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo _kbScroll.hide(); _reportSpamPanel.hide(); _toHistoryEnd.hide(); + _collapseComments.hide(); _attachDocument.hide(); _attachPhoto.hide(); _attachEmoji.hide(); @@ -3978,7 +4112,7 @@ void HistoryWidget::insertBotCommand(const QString &cmd) { } bool HistoryWidget::eventFilter(QObject *obj, QEvent *e) { - if (obj == &_toHistoryEnd && e->type() == QEvent::Wheel) { + if ((obj == &_toHistoryEnd || obj == &_collapseComments) && e->type() == QEvent::Wheel) { return _scroll.viewportEvent(e); } return TWidget::eventFilter(obj, e); @@ -4062,7 +4196,7 @@ bool HistoryWidget::canSendMessages(PeerData *peer) { } else if (peer->isChat()) { return !peer->asChat()->forbidden && !peer->asChat()->left; } else if (peer->isChannel()) { - return !peer->asChannel()->forbidden && !peer->asChannel()->left && peer->asChannel()->adminned; + return !peer->asChannel()->forbidden && !peer->asChannel()->left && (peer->asChannel()->adminned || !peer->asChannel()->isBroadcast); } } return false; @@ -4219,7 +4353,7 @@ void HistoryWidget::contextMenuEvent(QContextMenuEvent *e) { void HistoryWidget::deleteMessage() { HistoryItem *item = App::contextItem(); - if (!item || item->itemType() != HistoryItem::MsgType) return; + if (!item || item->type() != HistoryItemMsg) return; HistoryMessage *msg = dynamic_cast(item); App::main()->deleteLayer((msg && msg->uploading()) ? -2 : -1); @@ -4227,14 +4361,14 @@ void HistoryWidget::deleteMessage() { void HistoryWidget::forwardMessage() { HistoryItem *item = App::contextItem(); - if (!item || item->itemType() != HistoryItem::MsgType) return; + if (!item || item->type() != HistoryItemMsg || item->serviceMsg()) return; App::main()->forwardLayer(); } void HistoryWidget::selectMessage() { HistoryItem *item = App::contextItem(); - if (!item || item->itemType() != HistoryItem::MsgType) return; + if (!item || item->type() != HistoryItemMsg || item->serviceMsg()) return; if (_list) _list->selectItem(item); } @@ -4547,12 +4681,12 @@ void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) { flags |= MTPDmessage::flag_from_id; } if (img.type == ToPreparePhoto) { - h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(img.peer), MTPPeer(), MTPint(), MTP_int(img.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaPhoto(img.photo, MTP_string("")), MTPnullMarkup, MTPnullEntities, MTP_int(1))); + h->addNewMessage(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(img.peer), MTPPeer(), MTPint(), MTP_int(img.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaPhoto(img.photo, MTP_string("")), MTPnullMarkup, MTPnullEntities, MTP_int(1))); } else if (img.type == ToPrepareDocument) { - h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(img.peer), MTPPeer(), MTPint(), MTP_int(img.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaDocument(img.document), MTPnullMarkup, MTPnullEntities, MTP_int(1))); + h->addNewMessage(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(img.peer), MTPPeer(), MTPint(), MTP_int(img.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaDocument(img.document), MTPnullMarkup, MTPnullEntities, MTP_int(1))); } else if (img.type == ToPrepareAudio) { flags |= MTPDmessage_flag_media_unread; - h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(img.peer), MTPPeer(), MTPint(), MTP_int(img.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaAudio(img.audio), MTPnullMarkup, MTPnullEntities, MTP_int(1))); + h->addNewMessage(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(img.peer), MTPPeer(), MTPint(), MTP_int(img.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaAudio(img.audio), MTPnullMarkup, MTPnullEntities, MTP_int(1))); } if (_peer && img.peer == _peer->id) { @@ -4857,6 +4991,7 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { _field.resize(width() - _send.width() - _attachDocument.width() - _attachEmoji.width() - (kbShowShown ? _kbShow.width() : 0) - (_cmdStartShown ? _cmdStart.width() : 0), _field.height()); _toHistoryEnd.move((width() - _toHistoryEnd.width()) / 2, _scroll.y() + _scroll.height() - _toHistoryEnd.height() - st::historyToEndSkip); + updateCollapseCommentsVisibility(); _send.move(width() - _send.width(), _attachDocument.y()); _botStart.setGeometry(0, _attachDocument.y(), width(), _botStart.height()); @@ -4950,6 +5085,7 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, _scroll.resize(width(), newScrollHeight); _attachMention.setBoundings(_scroll.geometry()); _toHistoryEnd.move((width() - _toHistoryEnd.width()) / 2, _scroll.y() + _scroll.height() - _toHistoryEnd.height() - st::historyToEndSkip); + updateCollapseCommentsVisibility(); } if (!initial) { @@ -4984,7 +5120,7 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, _histInited = true; } - int32 toY = History::ScrollMax; + int32 toY = ScrollMax; if (initial && _history->lastWidth) { toY = newSt; _history->lastWidth = 0; @@ -5000,6 +5136,37 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, _animActiveTimer.start(AnimationTimerDelta); _activeAnimMsgId = _showAtMsgId; } + } else if (initial && _fixedInScrollMsgId > 0) { + HistoryItem *item = App::histItemById(_channel, _fixedInScrollMsgId); + if (!item || item->detached()) { + item = 0; + for (int32 blockIndex = 0, blocksCount = _history->blocks.size(); blockIndex < blocksCount; ++blockIndex) { + HistoryBlock *block = _history->blocks.at(blockIndex); + for (int32 itemIndex = 0, itemsCount = block->items.size(); itemIndex < itemsCount; ++itemIndex) { + item = block->items.at(itemIndex); + if (item->id > _fixedInScrollMsgId) { + break; + } else if (item->id < 0) { + if (item->type() == HistoryItemGroup && qMax(static_cast(item)->minId(), 1) >= _fixedInScrollMsgId) { + break; + } else if (item->type() == HistoryItemCollapse && static_cast(item)->wasMinId() >= _fixedInScrollMsgId) { + break; + } + } + } + } + if (item) { + toY = qMax(firstItemY + item->y + item->block()->y - _fixedInScrollMsgTop, 0); + } else { + _showAtMsgId = ShowAtUnreadMsgId; + _fixedInScrollMsgId = 0; + _fixedInScrollMsgTop = 0; + _histInited = false; + return updateListSize(addToY, initial); + } + } else { + toY = qMax(firstItemY + item->y + item->block()->y + item->height() - _fixedInScrollMsgTop, 0); + } } else if (initial && _history->unreadBar) { toY = firstItemY + _history->unreadBar->y + _history->unreadBar->block()->y; } else if (_history->showFrom) { @@ -5017,17 +5184,17 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, _scroll.scrollToY(toY); } -void HistoryWidget::addMessagesToFront(const QVector &messages) { +void HistoryWidget::addMessagesToFront(const QVector &messages, const QVector *collapsed) { int32 oldH = _history->height; - _list->messagesReceived(messages); + _list->messagesReceived(messages, collapsed); if (!_firstLoadRequest) { updateListSize(_history->height - oldH); updateBotKeyboard(); } } -void HistoryWidget::addMessagesToBack(const QVector &messages) { - _list->messagesReceivedDown(messages); +void HistoryWidget::addMessagesToBack(const QVector &messages, const QVector *collapsed) { + _list->messagesReceivedDown(messages, collapsed); if (!_firstLoadRequest) { updateListSize(0, false, true); } @@ -5038,22 +5205,7 @@ void HistoryWidget::countHistoryShowFrom() { _history->showFrom = 0; return; } - if (_history->showFrom) return; - - bool greaterFound = false; - for (History::const_iterator i = _history->cend(); i != _history->cbegin();) { - --i; - for (HistoryBlock::const_iterator j = (*i)->cend(); j != (*i)->cbegin();) { - --j; - if ((*j)->itemType() == HistoryItem::MsgType && (*j)->id > 0) { - if ((*j)->id >= _history->inboxReadBefore) { - _history->showFrom = *j; - } else { - return; - } - } - } - } + _history->updateShowFrom(); } void HistoryWidget::updateBotKeyboard() { @@ -5138,6 +5290,29 @@ void HistoryWidget::updateToEndVisibility() { } } +void HistoryWidget::updateCollapseCommentsVisibility() { + int32 collapseCommentsLeft = (width() - _collapseComments.width()) / 2, collapseCommentsTop = st::msgServiceMargin.top(); + bool collapseCommentsVisible = !_showAnim.animating() && _history && !_firstLoadRequest && _history->isChannel() && !_history->asChannelHistory()->onlyImportant(); + if (collapseCommentsVisible) { + if (HistoryItem *collapse = _history->asChannelHistory()->collapse()) { + int32 collapseY = (_list->height() - _history->height - st::historyPadding) + collapse->y + collapse->block()->y - _scroll.scrollTop(); + if (collapseY > _scroll.height()) { + collapseCommentsTop += qMin(collapseY - _scroll.height() - collapse->height(), 0); + } else { + collapseCommentsTop += qMax(collapseY, 0); + } + } + } + if (_collapseComments.x() != collapseCommentsLeft || _collapseComments.y() != collapseCommentsTop) { + _collapseComments.move(collapseCommentsLeft, collapseCommentsTop); + } + if (collapseCommentsVisible && _collapseComments.isHidden()) { + _collapseComments.show(); + } else if (!collapseCommentsVisible && !_collapseComments.isHidden()) { + _collapseComments.hide(); + } +} + void HistoryWidget::mousePressEvent(QMouseEvent *e) { _replyForwardPressed = QRect(0, _field.y() - st::sendPadding - st::replyHeight, st::replySkip, st::replyHeight).contains(e->pos()); if (_replyForwardPressed && !_replyForwardPreviewCancel.isHidden()) { @@ -5250,7 +5425,7 @@ void HistoryWidget::onStickerSend(DocumentData *sticker) { } else { flags |= MTPDmessage::flag_from_id; } - _history->addToBackDocument(newId.msg, flags, replyToId(), date(MTP_int(unixtime())), fromChannelName ? 0 : MTP::authedId(), sticker); + _history->addNewDocument(newId.msg, flags, replyToId(), date(MTP_int(unixtime())), fromChannelName ? 0 : MTP::authedId(), sticker); _history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_int(sendFlags), _peer->input, MTP_int(replyToId()), MTP_inputMediaDocument(MTP_inputDocument(MTP_long(sticker->id), MTP_long(sticker->access))), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId); App::main()->finishForwarding(_history); @@ -5571,10 +5746,6 @@ void HistoryWidget::onDeleteSelectedSure() { } } - if (!ids.isEmpty()) { - App::main()->deleteMessages(_peer, ids); - } - onClearSelected(); for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) { i.value()->destroy(); @@ -5583,22 +5754,34 @@ void HistoryWidget::onDeleteSelectedSure() { App::main()->itemResized(0); } App::wnd()->hideLayer(); + + if (!ids.isEmpty()) { + App::main()->deleteMessages(_peer, ids); + } } void HistoryWidget::onDeleteContextSure() { HistoryItem *item = App::contextItem(); - if (!item || item->itemType() != HistoryItem::MsgType) { + if (!item || item->type() != HistoryItemMsg) { return; } - if (item->id > 0) { - App::main()->deleteMessages(item->history()->peer, QVector(1, MTP_int(item->id))); - } + QVector toDelete(1, MTP_int(item->id)); + History *h = item->history(); + bool wasOnServer = (item->id > 0), wasLast = (h->lastMsg == item); item->destroy(); + if (!wasOnServer && wasLast && !h->lastMsg) { + App::main()->checkPeerHistory(h->peer); + } + if (App::main() && App::main()->peer() == peer()) { App::main()->itemResized(0); } App::wnd()->hideLayer(); + + if (wasOnServer) { + App::main()->deleteMessages(h->peer, toDelete); + } } void HistoryWidget::onListEscapePressed() { diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 0ccc2bb0d..7c302a00b 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -37,8 +37,8 @@ public: HistoryList(HistoryWidget *historyWidget, ScrollArea *scroll, History *history); - void messagesReceived(const QVector &messages); - void messagesReceivedDown(const QVector &messages); + void messagesReceived(const QVector &messages, const QVector *collapsed); + void messagesReceivedDown(const QVector &messages, const QVector *collapsed); bool event(QEvent *e); // calls touchEvent when necessary void touchEvent(QTouchEvent *e); @@ -84,6 +84,8 @@ public: bool wasSelectedText() const; void setFirstLoading(bool loading); + HistoryItem *atTopImportantMsg(int32 top, int32 height, int32 &bottomUnderScrollTop) const; + ~HistoryList(); public slots: @@ -115,7 +117,7 @@ private: void touchUpdateSpeed(); void touchDeaccelerate(int32 elapsed); - void adjustCurrent(int32 y); + void adjustCurrent(int32 y) const; HistoryItem *prevItem(HistoryItem *item); HistoryItem *nextItem(HistoryItem *item); void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting, bool force = false); @@ -129,7 +131,7 @@ private: HistoryWidget *historyWidget; ScrollArea *scrollArea; - int32 currentBlock, currentItem; + mutable int32 currentBlock, currentItem; bool _firstLoading; @@ -370,6 +372,14 @@ private: }; +class CollapseButton : public FlatButton { +public: + + CollapseButton(QWidget *parent); + void paintEvent(QPaintEvent *e); + +}; + class HistoryWidget : public TWidget, public RPCSender { Q_OBJECT @@ -449,6 +459,7 @@ public: PeerData *peer() const; MsgId msgId() const; + HistoryItem *atTopImportantMsg(int32 &bottomUnderScrollTop) const; void animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back = false); bool showStep(float64 ms); @@ -514,6 +525,7 @@ public: void contactsReceived(); void updateToEndVisibility(); + void updateCollapseCommentsVisibility(); void updateAfterDrag(); void ctrlEnterSubmitUpdated(); @@ -565,6 +577,7 @@ public slots: void onListScroll(); void onHistoryToEnd(); + void onCollapseComments(); void onSend(bool ctrlShiftEnter = false, MsgId replyTo = -1); void onUnblock(); void onBotStart(); @@ -653,8 +666,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); - void addMessagesToBack(const QVector &messages); + void addMessagesToFront(const QVector &messages, const QVector *collapsed); + void addMessagesToBack(const QVector &messages, const QVector *collapsed); void reportSpamDone(PeerData *peer, const MTPBool &result, mtpRequestId request); bool reportSpamFail(const RPCError &error, mtpRequestId request); @@ -683,7 +696,8 @@ private: PeerData *_peer, *_clearPeer; // cache _peer in _clearPeer when showing clear history box ChannelId _channel; bool _canSendMessages; - MsgId _showAtMsgId; + MsgId _showAtMsgId, _fixedInScrollMsgId; + int32 _fixedInScrollMsgTop; mtpRequestId _firstLoadRequest, _preloadRequest, _preloadDownRequest; @@ -698,6 +712,7 @@ private: bool _histInited; // initial updateListSize() called IconedButton _toHistoryEnd; + CollapseButton _collapseComments; MentionsDropdown _attachMention; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 60ecd2c64..f205353f2 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -543,7 +543,7 @@ void MainWidget::finishForwarding(History *hist) { if (genClientSideMessage) { FullMsgId newId(peerToChannel(hist->peer->id), clientMsgId()); HistoryMessage *msg = static_cast(_toForward.cbegin().value()); - hist->addToBackForwarded(newId.msg, date(MTP_int(unixtime())), hist->peer->isChannel() ? 0 : MTP::authedId(), msg); + hist->addNewForwarded(newId.msg, date(MTP_int(unixtime())), hist->peer->isChannel() ? 0 : MTP::authedId(), msg); if (HistorySticker *sticker = dynamic_cast(msg->getMedia())) { App::main()->incrementSticker(sticker->document()); } @@ -760,7 +760,10 @@ bool MainWidget::leaveChatFailed(PeerData *peer, const RPCError &error) { showDialogs(); } dialogs.removePeer(peer); - App::history(peer->id)->clear(); + if (History *h = App::historyLoaded(peer->id)) { + h->clear(); + h->newLoaded = h->oldLoaded = true; + } MTP::send(MTPmessages_DeleteHistory(peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, peer)); return true; } @@ -773,7 +776,10 @@ void MainWidget::deleteHistoryAfterLeave(PeerData *peer, const MTPUpdates &updat showDialogs(); } dialogs.removePeer(peer); - App::history(peer->id)->clear(); + if (History *h = App::historyLoaded(peer->id)) { + h->clear(); + h->newLoaded = h->oldLoaded = true; + } MTP::send(MTPmessages_DeleteHistory(peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, peer)); } @@ -809,20 +815,22 @@ void MainWidget::deletedContact(UserData *user, const MTPcontacts_Link &result) void MainWidget::deleteConversation(PeerData *peer) { dialogs.removePeer(peer); - History *h = App::history(peer->id); - h->clear(); - h->newLoaded = h->oldLoaded = true; + if (History *h = App::historyLoaded(peer->id)) { + h->clear(); + h->newLoaded = h->oldLoaded = true; + } showDialogs(); MTP::send(MTPmessages_DeleteHistory(peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, peer)); } void MainWidget::clearHistory(PeerData *peer) { - History *h = App::history(peer->id); - if (h->lastMsg) { - Local::addSavedPeer(h->peer, h->lastMsg->date); + if (History *h = App::historyLoaded(peer->id)) { + if (h->lastMsg) { + Local::addSavedPeer(h->peer, h->lastMsg->date); + } + h->clear(); + h->newLoaded = h->oldLoaded = true; } - h->clear(); - h->newLoaded = h->oldLoaded = true; showPeerHistory(peer->id, ShowAtUnreadMsgId); MTP::send(MTPmessages_DeleteHistory(peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, peer)); } @@ -919,7 +927,7 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu if (peer && peer->isChannel()) { peer->asChannel()->ptsReceived(d.vpts.v); } else { - LOG(("App Error: received messages.channelMessages in MainWidget::checkedHistory when no channel was passed!")); + LOG(("API Error: received messages.channelMessages when no channel was passed! (MainWidget::checkedHistory)")); } App::feedUsers(d.vusers); @@ -930,11 +938,11 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu if (!v) return; if (v->isEmpty()) { - if ((profile && profile->peer() == peer) || (overview && overview->peer() == peer) || _stack.contains(peer) || history.peer() == peer) { - showDialogs(); - } if (peer->isChat() && peer->asChat()->left) { dialogs.removePeer(peer); + if (history.peer() == peer) { + showDialogs(); + } } else { History *h = App::historyLoaded(peer->id); if (h) Local::addSavedPeer(peer, h->lastMsgDate); @@ -942,7 +950,7 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu } else { History *h = App::historyLoaded(peer->id); if (!h->lastMsg) { - h->addToBack((*v)[0], 0); + h->addNewMessage((*v)[0], 0); } } } @@ -975,6 +983,7 @@ bool MainWidget::sendMessageFail(const RPCError &error) { } void MainWidget::onResendAsDocument() { + QMap historiesToCheck; QList tmp = _resendImgRandomIds; _resendImgRandomIds.clear(); for (int32 i = 0, l = tmp.size(); i < l; ++i) { @@ -989,20 +998,33 @@ void MainWidget::onResendAsDocument() { } } } + History *h = item->history(); + bool wasLast = (h->lastMsg == item); item->destroy(); + if (wasLast && !h->lastMsg) historiesToCheck.insert(h, true); } } + for (QMap::const_iterator i = historiesToCheck.cbegin(), e = historiesToCheck.cend(); i != e; ++i) { + checkPeerHistory(i.key()->peer); + } App::wnd()->hideLayer(true); } void MainWidget::onCancelResend() { + QMap historiesToCheck; QList tmp = _resendImgRandomIds; _resendImgRandomIds.clear(); for (int32 i = 0, l = tmp.size(); i < l; ++i) { if (HistoryItem *item = App::histItemById(App::histItemByRandom(tmp.at(i)))) { + History *h = item->history(); + bool wasLast = (h->lastMsg == item); item->destroy(); + if (wasLast && !h->lastMsg) historiesToCheck.insert(h, true); } } + for (QMap::const_iterator i = historiesToCheck.cbegin(), e = historiesToCheck.cend(); i != e; ++i) { + checkPeerHistory(i.key()->peer); + } } void MainWidget::onCacheBackground() { @@ -1149,7 +1171,7 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl flags |= MTPDmessage::flag_from_id; } MTPVector localEntities = linksToMTP(textParseLinks(sendingText, itemTextParseOptions(hist, App::self()).flags)); - hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(hist->peer->id), MTPPeer(), MTPint(), MTP_int(replyTo), MTP_int(unixtime()), msgText, media, MTPnullMarkup, localEntities, MTP_int(1))); + hist->addNewMessage(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(hist->peer->id), MTPPeer(), MTPint(), MTP_int(replyTo), MTP_int(unixtime()), msgText, media, MTPnullMarkup, localEntities, MTP_int(1))); hist->sendRequestId = MTP::send(MTPmessages_SendMessage(MTP_int(sendFlags), hist->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId), MTPnullMarkup, localEntities), rpcDone(&MainWidget::sentUpdatesReceived, randomId), rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId); } @@ -1157,7 +1179,9 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl } void MainWidget::sendMessage(History *hist, const QString &text, MsgId replyTo) { - hist->getReadyFor(ShowAtTheEndMsgId); + MsgId fixInScrollMsgId = 0; + int32 fixInScrollMsgTop = 0; + hist->getReadyFor(ShowAtTheEndMsgId, fixInScrollMsgId, fixInScrollMsgTop); readServerHistory(hist, false); sendPreparedText(hist, history.prepareMessage(text), replyTo); } @@ -1233,7 +1257,7 @@ void MainWidget::preloadOverviews(PeerData *peer) { History *h = App::history(peer->id); bool sending[OverviewCount] = { false }; for (int32 i = 0; i < OverviewCount; ++i) { - if (h->_overviewCount[i] < 0) { + if (h->overviewCount[i] < 0) { if (_overviewPreload[i].constFind(peer) == _overviewPreload[i].cend()) { sending[i] = true; } @@ -1274,14 +1298,14 @@ void MainWidget::overviewPreloaded(PeerData *peer, const MTPmessages_Messages &r const MTPDmessages_messages &d(result.c_messages_messages()); App::feedUsers(d.vusers); App::feedChats(d.vchats); - h->_overviewCount[type] = d.vmessages.c_vector().v.size(); + h->overviewCount[type] = d.vmessages.c_vector().v.size(); } break; case mtpc_messages_messagesSlice: { const MTPDmessages_messagesSlice &d(result.c_messages_messagesSlice()); App::feedUsers(d.vusers); App::feedChats(d.vchats); - h->_overviewCount[type] = d.vcount.v; + h->overviewCount[type] = d.vcount.v; } break; case mtpc_messages_channelMessages: { @@ -1289,21 +1313,24 @@ void MainWidget::overviewPreloaded(PeerData *peer, const MTPmessages_Messages &r if (peer && peer->isChannel()) { peer->asChannel()->ptsReceived(d.vpts.v); } else { - LOG(("App Error: received messages.channelMessages in MainWidget::overviewPreloaded when no channel was passed!")); + LOG(("API Error: received messages.channelMessages when no channel was passed! (MainWidget::overviewPreloaded)")); + } + if (d.has_collapsed()) { // should not be returned + LOG(("API Error: channels.getMessages and messages.getMessages should not return collapsed groups! (MainWidget::overviewPreloaded)")); } App::feedUsers(d.vusers); App::feedChats(d.vchats); - h->_overviewCount[type] = d.vcount.v; + h->overviewCount[type] = d.vcount.v; } break; default: return; } - if (h->_overviewCount[type] > 0) { - for (History::MediaOverviewIds::const_iterator i = h->_overviewIds[type].cbegin(), e = h->_overviewIds[type].cend(); i != e; ++i) { + if (h->overviewCount[type] > 0) { + for (History::MediaOverviewIds::const_iterator i = h->overviewIds[type].cbegin(), e = h->overviewIds[type].cend(); i != e; ++i) { if (i.key() < 0) { - ++h->_overviewCount[type]; + ++h->overviewCount[type]; } else { break; } @@ -1323,7 +1350,7 @@ void MainWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) { History *h = peer ? App::historyLoaded(peer->id) : 0; if (h) { for (int32 i = 0; i < OverviewCount; ++i) { - if (!h->_overview[i].isEmpty() || h->_overviewCount[i] > 0 || i == overview->type()) { + if (!h->overview[i].isEmpty() || h->overviewCount[i] > 0 || i == overview->type()) { mask |= (1 << i); } } @@ -1420,20 +1447,20 @@ void MainWidget::loadMediaBack(PeerData *peer, MediaOverviewType type, bool many MsgId minId = 0; History *hist = App::history(peer->id); - if (hist->_overviewCount[type] == 0) return; // all loaded + if (hist->overviewCount[type] == 0) return; // all loaded - for (History::MediaOverviewIds::const_iterator i = hist->_overviewIds[type].cbegin(), e = hist->_overviewIds[type].cend(); i != e; ++i) { + for (History::MediaOverviewIds::const_iterator i = hist->overviewIds[type].cbegin(), e = hist->overviewIds[type].cend(); i != e; ++i) { if (i.key() > 0) { minId = i.key(); break; } } - int32 limit = many ? SearchManyPerPage : (hist->_overview[type].size() > MediaOverviewStartPerPage) ? SearchPerPage : MediaOverviewStartPerPage; + int32 limit = many ? SearchManyPerPage : (hist->overview[type].size() > MediaOverviewStartPerPage) ? SearchPerPage : MediaOverviewStartPerPage; MTPMessagesFilter filter = typeToMediaFilter(type); if (type == OverviewCount) return; int32 flags = peer->isChannel() ? MTPmessages_Search_flag_only_important : 0; - _overviewLoad[type].insert(peer, MTP::send(MTPmessages_Search(MTP_int(flags), peer->input, MTPstring(), filter, MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(minId), MTP_int(limit)), rpcDone(&MainWidget::photosLoaded, hist))); + _overviewLoad[type].insert(peer, MTP::send(MTPmessages_Search(MTP_int(flags), peer->input, MTPstring(), filter, MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(minId), MTP_int(limit)), rpcDone(&MainWidget::overviewLoaded, hist))); } void MainWidget::peerUsernameChanged(PeerData *peer) { @@ -1461,7 +1488,7 @@ void MainWidget::showNewGroup() { dialogs.onNewGroup(); } -void MainWidget::photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req) { +void MainWidget::overviewLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req) { OverviewsPreload::iterator it; MediaOverviewType type = OverviewCount; for (int32 i = 0; i < OverviewCount; ++i) { @@ -1481,14 +1508,14 @@ void MainWidget::photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpR App::feedUsers(d.vusers); App::feedChats(d.vchats); v = &d.vmessages.c_vector().v; - h->_overviewCount[type] = 0; + h->overviewCount[type] = 0; } break; case mtpc_messages_messagesSlice: { const MTPDmessages_messagesSlice &d(msgs.c_messages_messagesSlice()); App::feedUsers(d.vusers); App::feedChats(d.vchats); - h->_overviewCount[type] = d.vcount.v; + h->overviewCount[type] = d.vcount.v; v = &d.vmessages.c_vector().v; } break; @@ -1497,36 +1524,39 @@ void MainWidget::photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpR if (h && h->peer->isChannel()) { h->peer->asChannel()->ptsReceived(d.vpts.v); } else { - LOG(("App Error: received messages.channelMessages in MainWidget::photosLoaded when no channel was passed!")); + LOG(("API Error: received messages.channelMessages when no channel was passed! (MainWidget::overviewLoaded)")); + } + if (d.has_collapsed()) { // should not be returned + LOG(("API Error: channels.getMessages and messages.getMessages should not return collapsed groups! (MainWidget::overviewLoaded)")); } App::feedUsers(d.vusers); App::feedChats(d.vchats); - h->_overviewCount[type] = d.vcount.v; + h->overviewCount[type] = d.vcount.v; v = &d.vmessages.c_vector().v; } break; default: return; } - if (h->_overviewCount[type] > 0) { - for (History::MediaOverviewIds::const_iterator i = h->_overviewIds[type].cbegin(), e = h->_overviewIds[type].cend(); i != e; ++i) { + if (h->overviewCount[type] > 0) { + for (History::MediaOverviewIds::const_iterator i = h->overviewIds[type].cbegin(), e = h->overviewIds[type].cend(); i != e; ++i) { if (i.key() < 0) { - ++h->_overviewCount[type]; + ++h->overviewCount[type]; } else { break; } } } if (v->isEmpty()) { - h->_overviewCount[type] = 0; + h->overviewCount[type] = 0; } for (QVector::const_iterator i = v->cbegin(), e = v->cend(); i != e; ++i) { - HistoryItem *item = App::histories().addToBack(*i, -1); - if (item && h->_overviewIds[type].constFind(item->id) == h->_overviewIds[type].cend()) { - h->_overviewIds[type].insert(item->id, NullType()); - h->_overview[type].push_front(item->id); + HistoryItem *item = App::histories().addNewMessage(*i, -1); + if (item && h->overviewIds[type].constFind(item->id) == h->overviewIds[type].cend()) { + h->overviewIds[type].insert(item->id, NullType()); + h->overview[type].push_front(item->id); } } if (App::wnd()) App::wnd()->mediaOverviewUpdated(h->peer, type); @@ -1580,6 +1610,11 @@ void MainWidget::messagesAffected(PeerData *peer, const MTPmessages_AffectedMess } else { ptsUpdated(d.vpts.v, d.vpts_count.v); } + if (History *h = App::historyLoaded(peer->id)) { + if (!h->lastMsg) { + checkPeerHistory(peer); + } + } } void MainWidget::videoLoadProgress(mtpFileLoader *loader) { @@ -1952,7 +1987,7 @@ void MainWidget::serviceNotification(const QString &msg, const MTPMessageMedia & HistoryItem *item = 0; while (textSplit(sendingText, leftText, MaxMessageSize)) { MTPVector localEntities = linksToMTP(textParseLinks(sendingText, _historyTextOptions.flags)); - item = App::histories().addToBack(MTP_message(MTP_int(flags), MTP_int(clientMsgId()), MTP_int(ServiceUserId), MTP_peerUser(MTP_int(MTP::authedId())), MTPPeer(), MTPint(), MTPint(), MTP_int(unixtime()), MTP_string(sendingText), media, MTPnullMarkup, localEntities, MTPint()), unread ? 1 : 2); + item = App::histories().addNewMessage(MTP_message(MTP_int(flags), MTP_int(clientMsgId()), MTP_int(ServiceUserId), MTP_peerUser(MTP_int(MTP::authedId())), MTPPeer(), MTPint(), MTPint(), MTP_int(unixtime()), MTP_string(sendingText), media, MTPnullMarkup, localEntities, MTPint()), unread ? 1 : 2); } if (item) { history.peerMessagesUpdated(item->history()->peer->id); @@ -1977,7 +2012,10 @@ void MainWidget::serviceHistoryDone(const MTPmessages_Messages &msgs) { case mtpc_messages_channelMessages: { const MTPDmessages_channelMessages &d(msgs.c_messages_channelMessages()); - LOG(("App Error: received messages.channelMessages in MainWidget::serviceHistoryDone!")); + LOG(("API Error: received messages.channelMessages! (MainWidget::serviceHistoryDone)")); + if (d.has_collapsed()) { // should not be returned + LOG(("API Error: channels.getMessages and messages.getMessages should not return collapsed groups! (MainWidget::serviceHistoryDone)")); + } App::feedUsers(d.vusers); App::feedChats(d.vchats); @@ -2112,6 +2150,10 @@ void MainWidget::setInnerFocus() { } } +HistoryItem *MainWidget::atTopImportantMsg(int32 &bottomUnderScrollTop) const { + return history.atTopImportantMsg(bottomUnderScrollTop); +} + void MainWidget::createDialogAtTop(History *history, int32 unreadCount) { dialogs.createDialogAtTop(history, unreadCount); } @@ -2844,6 +2886,10 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha h->setNotLoadedAtBottom(); if (history.peer() == channel) { history.updateToEndVisibility(); + if (d.vunread_count.v >= h->asChannelHistory()->unreadCountAll) { + h->asChannelHistory()->unreadCountAll = d.vunread_count.v; + h->inboxReadBefore = d.vread_inbox_max_id.v + 1; + } } } @@ -3746,7 +3792,7 @@ void MainWidget::handleUpdates(const MTPUpdates &updates, uint64 randomId) { } int32 flags = d.vflags.v | MTPDmessage::flag_from_id; bool out = (flags & MTPDmessage_flag_out); - HistoryItem *item = App::histories().addToBack(MTP_message(MTP_int(flags), d.vid, out ? MTP_int(MTP::authedId()) : d.vuser_id, MTP_peerUser(out ? d.vuser_id : MTP_int(MTP::authedId())), d.vfwd_from_id, d.vfwd_date, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty(), MTPnullMarkup, d.has_entities() ? d.ventities : MTPnullEntities, MTPint())); + HistoryItem *item = App::histories().addNewMessage(MTP_message(MTP_int(flags), d.vid, out ? MTP_int(MTP::authedId()) : d.vuser_id, MTP_peerUser(out ? d.vuser_id : MTP_int(MTP::authedId())), d.vfwd_from_id, d.vfwd_date, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty(), MTPnullMarkup, d.has_entities() ? d.ventities : MTPnullEntities, MTPint())); if (item) { history.peerMessagesUpdated(item->history()->peer->id); } @@ -3767,7 +3813,7 @@ void MainWidget::handleUpdates(const MTPUpdates &updates, uint64 randomId) { } int32 flags = d.vflags.v | MTPDmessage::flag_from_id; bool out = (flags & MTPDmessage_flag_out); - HistoryItem *item = App::histories().addToBack(MTP_message(MTP_int(flags), d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), d.vfwd_from_id, d.vfwd_date, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty(), MTPnullMarkup, d.has_entities() ? d.ventities : MTPnullEntities, MTPint())); + HistoryItem *item = App::histories().addNewMessage(MTP_message(MTP_int(flags), d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), d.vfwd_from_id, d.vfwd_date, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty(), MTPnullMarkup, d.has_entities() ? d.ventities : MTPnullEntities, MTPint())); if (item) { history.peerMessagesUpdated(item->history()->peer->id); } @@ -3830,7 +3876,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { App::checkEntitiesAndViewsUpdate(d.vmessage.c_message()); } - HistoryItem *item = App::histories().addToBack(d.vmessage); + HistoryItem *item = App::histories().addNewMessage(d.vmessage); if (item) { history.peerMessagesUpdated(item->history()->peer->id); } @@ -3845,14 +3891,14 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { App::historyUnregItem(msgRow); History *h = msgRow->history(); for (int32 i = 0; i < OverviewCount; ++i) { - History::MediaOverviewIds::iterator j = h->_overviewIds[i].find(msgRow->id); - if (j != h->_overviewIds[i].cend()) { - h->_overviewIds[i].erase(j); - if (h->_overviewIds[i].constFind(d.vid.v) == h->_overviewIds[i].cend()) { - h->_overviewIds[i].insert(d.vid.v, NullType()); - for (int32 k = 0, l = h->_overview[i].size(); k != l; ++k) { - if (h->_overview[i].at(k) == msgRow->id) { - h->_overview[i][k] = d.vid.v; + History::MediaOverviewIds::iterator j = h->overviewIds[i].find(msgRow->id); + if (j != h->overviewIds[i].cend()) { + h->overviewIds[i].erase(j); + if (h->overviewIds[i].constFind(d.vid.v) == h->overviewIds[i].cend()) { + h->overviewIds[i].insert(d.vid.v, NullType()); + for (int32 k = 0, l = h->overview[i].size(); k != l; ++k) { + if (h->overview[i].at(k) == msgRow->id) { + h->overview[i][k] = d.vid.v; break; } } @@ -3864,7 +3910,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { if (!App::historyRegItem(msgRow)) { msgUpdated(h->peer->id, msgRow); } else { + bool wasLast = (h->lastMsg == msgRow); msgRow->destroy(); + if (wasLast && !h->lastMsg) { + checkPeerHistory(h->peer); + } history.peerMessagesUpdated(); } } @@ -4038,7 +4088,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { UserData *user = App::userLoaded(d.vuser_id.v); if (user) { if (App::history(user->id)->loadedAtBottom()) { - App::history(user->id)->addToBackService(clientMsgId(), date(d.vdate), lng_action_user_registered(lt_from, user->name), MTPDmessage_flag_unread); + App::history(user->id)->addNewService(clientMsgId(), date(d.vdate), lng_action_user_registered(lt_from, user->name), MTPDmessage_flag_unread); } } } break; @@ -4131,7 +4181,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { App::checkEntitiesAndViewsUpdate(d.vmessage.c_message()); } - HistoryItem *item = App::histories().addToBack(d.vmessage); + HistoryItem *item = App::histories().addNewMessage(d.vmessage); if (item) { history.peerMessagesUpdated(item->history()->peer->id); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index ffe6471ee..72e7a31f2 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -389,6 +389,8 @@ public: void ctrlEnterSubmitUpdated(); void setInnerFocus(); + HistoryItem *atTopImportantMsg(int32 &bottomUnderScrollTop) const; + ~MainWidget(); signals: @@ -466,7 +468,7 @@ private: void readRequestDone(PeerData *peer); void messagesAffected(PeerData *peer, const MTPmessages_AffectedMessages &result); - void photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req); + void overviewLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req); bool _started; diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 55bc78225..44cf2ad20 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -150,8 +150,8 @@ void MediaView::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) { if (!_photo && !_doc) return; if (_history && _history->peer == peer && type == _overview) { _index = -1; - for (int i = 0, l = _history->_overview[_overview].size(); i < l; ++i) { - if (_history->_overview[_overview].at(i) == _msgid) { + for (int i = 0, l = _history->overview[_overview].size(); i < l; ++i) { + if (_history->overview[_overview].at(i) == _msgid) { _index = i; break; } @@ -290,13 +290,13 @@ void MediaView::updateControls() { } updateHeader(); if (_photo || (_history && _overview == OverviewPhotos)) { - _leftNavVisible = (_index > 0) || (_index == 0 && _history && _history->_overview[_overview].size() < _history->_overviewCount[_overview]); + _leftNavVisible = (_index > 0) || (_index == 0 && _history && _history->overview[_overview].size() < _history->overviewCount[_overview]); _rightNavVisible = (_index >= 0) && ( - (_history && _index + 1 < _history->_overview[_overview].size()) || + (_history && _index + 1 < _history->overview[_overview].size()) || (_user && (_index + 1 < _user->photos.size() || _index + 1 < _user->photosCount))); } else if (_history && _overview == OverviewDocuments) { - _leftNavVisible = (_index > 0) || (_index == 0 && _history && _history->_overview[_overview].size() < _history->_overviewCount[_overview]); - _rightNavVisible = (_index >= 0) && _history && (_index + 1 < _history->_overview[_overview].size()); + _leftNavVisible = (_index > 0) || (_index == 0 && _history && _history->overview[_overview].size() < _history->overviewCount[_overview]); + _rightNavVisible = (_index >= 0) && _history && (_index + 1 < _history->overview[_overview].size()); } else { _leftNavVisible = _rightNavVisible = false; } @@ -739,14 +739,14 @@ void MediaView::showDocument(DocumentData *doc, HistoryItem *context) { if (_history) { _overview = OverviewDocuments; - for (int i = 0, l = _history->_overview[_overview].size(); i < l; ++i) { - if (_history->_overview[_overview].at(i) == _msgid) { + for (int i = 0, l = _history->overview[_overview].size(); i < l; ++i) { + if (_history->overview[_overview].at(i) == _msgid) { _index = i; break; } } - if (_history->_overviewCount[_overview] < 0) { + if (_history->overviewCount[_overview] < 0) { loadBack(); } } @@ -1391,9 +1391,9 @@ void MediaView::moveToNext(int32 delta) { int32 newIndex = _index + delta; if (_history && _overview != OverviewCount) { - if (newIndex >= 0 && newIndex < _history->_overview[_overview].size()) { + if (newIndex >= 0 && newIndex < _history->overview[_overview].size()) { _index = newIndex; - if (HistoryItem *item = App::histItemById(_history->channelId(), _history->_overview[_overview][_index])) { + if (HistoryItem *item = App::histItemById(_history->channelId(), _history->overview[_overview][_index])) { _msgid = item->id; _channel = item->channelId(); _canForward = _msgid > 0 && (_channel == NoChannel); @@ -1432,8 +1432,8 @@ void MediaView::preloadData(int32 delta) { if (from > to) qSwap(from, to); if (_history && _overview != OverviewCount) { for (int32 i = from; i <= to; ++i) { - if (i >= 0 && i < _history->_overview[_overview].size() && i != _index) { - if (HistoryItem *item = App::histItemById(_history->channelId(), _history->_overview[_overview][i])) { + if (i >= 0 && i < _history->overview[_overview].size() && i != _index) { + if (HistoryItem *item = App::histItemById(_history->channelId(), _history->overview[_overview][i])) { if (HistoryMedia *media = item->getMedia()) { switch (media->type()) { case MediaTypePhoto: static_cast(media)->photo()->full->load(); break; @@ -1444,8 +1444,8 @@ void MediaView::preloadData(int32 delta) { } } } - if (forget >= 0 && forget < _history->_overview[_overview].size() && forget != _index) { - if (HistoryItem *item = App::histItemById(_history->channelId(), _history->_overview[_overview][forget])) { + if (forget >= 0 && forget < _history->overview[_overview].size() && forget != _index) { + if (HistoryItem *item = App::histItemById(_history->channelId(), _history->overview[_overview][forget])) { if (HistoryMedia *media = item->getMedia()) { switch (media->type()) { case MediaTypePhoto: static_cast(media)->photo()->forget(); break; @@ -1844,14 +1844,14 @@ void MediaView::updateImage() { } void MediaView::findCurrent() { - for (int i = 0, l = _history->_overview[_overview].size(); i < l; ++i) { - if (_history->_overview[_overview].at(i) == _msgid) { + for (int i = 0, l = _history->overview[_overview].size(); i < l; ++i) { + if (_history->overview[_overview].at(i) == _msgid) { _index = i; break; } } - if (_history->_overviewCount[_overview] < 0 || (!_index && _history->_overviewCount[_overview] > 0)) { + if (_history->overviewCount[_overview] < 0 || (!_index && _history->overviewCount[_overview] > 0)) { loadBack(); } } @@ -1859,7 +1859,7 @@ void MediaView::findCurrent() { void MediaView::loadBack() { if (_loadRequest || _index < 0 || (_overview == OverviewCount && !_user)) return; - if (_history && _overview != OverviewCount && _history->_overviewCount[_overview] != 0) { + if (_history && _overview != OverviewCount && _history->overviewCount[_overview] != 0) { if (App::main()) App::main()->loadMediaBack(_history->peer, _overview); } else if (_user && _user->photosCount != 0) { int32 limit = (_index < MediaOverviewStartPerPage && _user->photos.size() > MediaOverviewStartPerPage) ? SearchPerPage : MediaOverviewStartPerPage; @@ -1907,8 +1907,8 @@ void MediaView::updateHeader() { int32 index = _index, count = 0; if (_history) { if (_overview != OverviewCount) { - count = _history->_overviewCount[_overview] ? _history->_overviewCount[_overview] : _history->_overview[_overview].size(); - if (index >= 0) index += count - _history->_overview[_overview].size(); + count = _history->overviewCount[_overview] ? _history->overviewCount[_overview] : _history->overview[_overview].size(); + if (index >= 0) index += count - _history->overview[_overview].size(); } } else if (_user) { count = _user->photosCount ? _user->photosCount : _user->photos.size(); diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index f6bb18db8..8bdfe54de 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -104,7 +104,6 @@ private: void findCurrent(); void loadBack(); - void photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req); void userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mtpRequestId req); void filesLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req); diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.cpp b/Telegram/SourceFiles/mtproto/mtpScheme.cpp index bf7bd42c3..1e0d64be7 100644 --- a/Telegram/SourceFiles/mtproto/mtpScheme.cpp +++ b/Telegram/SourceFiles/mtproto/mtpScheme.cpp @@ -1656,19 +1656,6 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP } break; - case mtpc_messageActionChannelToggleComments: - if (stage) { - to.add(",\n").addSpaces(lev); - } else { - to.add("{ messageActionChannelToggleComments"); - to.add("\n").addSpaces(lev); - } - switch (stage) { - case 0: 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; - } - break; - case mtpc_dialog: if (stage) { to.add(",\n").addSpaces(lev); @@ -4599,7 +4586,9 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP switch (stage) { case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 1: to.add(" channel_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 2: to.add(" self_participant: "); ++stages.back(); if (flag & MTPDchannelParticipants::flag_self_participant) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break; + case 2: to.add(" participants_count: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 3: to.add(" self_participant: "); ++stages.back(); if (flag & MTPDchannelParticipants::flag_self_participant) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break; + case 4: to.add(" participants: "); ++stages.back(); if (flag & MTPDchannelParticipants::flag_participants) { types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 1 IN FIELD flags ]"); } break; default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; } break; @@ -4722,6 +4711,20 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP } 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 1: to.add(" date: "); ++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; + } + break; + + case mtpc_channelParticipantSelf: + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ channelParticipantSelf"); + 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 1: to.add(" inviter_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 2: to.add(" date: "); ++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; diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.h b/Telegram/SourceFiles/mtproto/mtpScheme.h index fa3082ade..520287354 100644 --- a/Telegram/SourceFiles/mtproto/mtpScheme.h +++ b/Telegram/SourceFiles/mtproto/mtpScheme.h @@ -166,7 +166,6 @@ enum { mtpc_messageActionChatDeleteUser = 0xb2ae9b0c, mtpc_messageActionChatJoinedByLink = 0xf89cf5e8, mtpc_messageActionChannelCreate = 0x95d2ac92, - mtpc_messageActionChannelToggleComments = 0xf2863903, mtpc_dialog = 0xc1dd804a, mtpc_dialogChannel = 0x5b8496b2, mtpc_photoEmpty = 0x2331b22d, @@ -402,7 +401,7 @@ enum { mtpc_inputChannelEmpty = 0xee8c1e86, mtpc_inputChannel = 0xafeb712e, mtpc_contacts_resolvedPeer = 0x7f077ad9, - mtpc_channelParticipants = 0x57d3e762, + mtpc_channelParticipants = 0xdee6d213, mtpc_messageRange = 0xae30253, mtpc_messageGroup = 0xe8346f53, mtpc_updates_channelDifferenceEmpty = 0x3e11affb, @@ -411,7 +410,8 @@ enum { mtpc_channelMessagesFilterEmpty = 0x94d42ee7, mtpc_channelMessagesFilter = 0xcd77d957, mtpc_channelMessagesFilterCollapsed = 0xfa01232e, - mtpc_channelParticipant = 0x506116ea, + mtpc_channelParticipant = 0x15ebac1d, + mtpc_channelParticipantSelf = 0xa3289a6d, mtpc_channelParticipantModerator = 0x91057fef, mtpc_channelParticipantEditor = 0x98192d61, mtpc_channelParticipantKicked = 0x8cc5e69a, @@ -766,7 +766,6 @@ class MTPDmessageActionChatAddUser; class MTPDmessageActionChatDeleteUser; class MTPDmessageActionChatJoinedByLink; class MTPDmessageActionChannelCreate; -class MTPDmessageActionChannelToggleComments; class MTPdialog; class MTPDdialog; @@ -1156,6 +1155,7 @@ class MTPDchannelMessagesFilter; class MTPchannelParticipant; class MTPDchannelParticipant; +class MTPDchannelParticipantSelf; class MTPDchannelParticipantModerator; class MTPDchannelParticipantEditor; class MTPDchannelParticipantKicked; @@ -3728,18 +3728,6 @@ public: return *(const MTPDmessageActionChannelCreate*)data; } - MTPDmessageActionChannelToggleComments &_messageActionChannelToggleComments() { - if (!data) throw mtpErrorUninitialized(); - if (_type != mtpc_messageActionChannelToggleComments) throw mtpErrorWrongTypeId(_type, mtpc_messageActionChannelToggleComments); - split(); - return *(MTPDmessageActionChannelToggleComments*)data; - } - const MTPDmessageActionChannelToggleComments &c_messageActionChannelToggleComments() const { - if (!data) throw mtpErrorUninitialized(); - if (_type != mtpc_messageActionChannelToggleComments) throw mtpErrorWrongTypeId(_type, mtpc_messageActionChannelToggleComments); - return *(const MTPDmessageActionChannelToggleComments*)data; - } - uint32 innerLength() const; mtpTypeId type() const; void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons); @@ -3756,7 +3744,6 @@ private: explicit MTPmessageAction(MTPDmessageActionChatDeleteUser *_data); explicit MTPmessageAction(MTPDmessageActionChatJoinedByLink *_data); explicit MTPmessageAction(MTPDmessageActionChannelCreate *_data); - explicit MTPmessageAction(MTPDmessageActionChannelToggleComments *_data); friend MTPmessageAction MTP_messageActionEmpty(); friend MTPmessageAction MTP_messageActionChatCreate(const MTPstring &_title, const MTPVector &_users); @@ -3767,7 +3754,6 @@ private: friend MTPmessageAction MTP_messageActionChatDeleteUser(MTPint _user_id); friend MTPmessageAction MTP_messageActionChatJoinedByLink(MTPint _inviter_id); friend MTPmessageAction MTP_messageActionChannelCreate(const MTPstring &_title); - friend MTPmessageAction MTP_messageActionChannelToggleComments(MTPBool _enabled); mtpTypeId _type; }; @@ -8277,7 +8263,7 @@ public: private: explicit MTPchannelParticipants(MTPDchannelParticipants *_data); - friend MTPchannelParticipants MTP_channelParticipants(MTPint _flags, MTPint _channel_id, const MTPChannelParticipant &_self_participant); + friend MTPchannelParticipants MTP_channelParticipants(MTPint _flags, MTPint _channel_id, MTPint _participants_count, const MTPChannelParticipant &_self_participant, const MTPVector &_participants); }; typedef MTPBoxed MTPChannelParticipants; @@ -8467,6 +8453,18 @@ public: return *(const MTPDchannelParticipant*)data; } + MTPDchannelParticipantSelf &_channelParticipantSelf() { + if (!data) throw mtpErrorUninitialized(); + if (_type != mtpc_channelParticipantSelf) throw mtpErrorWrongTypeId(_type, mtpc_channelParticipantSelf); + split(); + return *(MTPDchannelParticipantSelf*)data; + } + const MTPDchannelParticipantSelf &c_channelParticipantSelf() const { + if (!data) throw mtpErrorUninitialized(); + if (_type != mtpc_channelParticipantSelf) throw mtpErrorWrongTypeId(_type, mtpc_channelParticipantSelf); + return *(const MTPDchannelParticipantSelf*)data; + } + MTPDchannelParticipantModerator &_channelParticipantModerator() { if (!data) throw mtpErrorUninitialized(); if (_type != mtpc_channelParticipantModerator) throw mtpErrorWrongTypeId(_type, mtpc_channelParticipantModerator); @@ -8525,12 +8523,14 @@ public: private: explicit MTPchannelParticipant(mtpTypeId type); explicit MTPchannelParticipant(MTPDchannelParticipant *_data); + explicit MTPchannelParticipant(MTPDchannelParticipantSelf *_data); explicit MTPchannelParticipant(MTPDchannelParticipantModerator *_data); explicit MTPchannelParticipant(MTPDchannelParticipantEditor *_data); explicit MTPchannelParticipant(MTPDchannelParticipantKicked *_data); explicit MTPchannelParticipant(MTPDchannelParticipantCreator *_data); - friend MTPchannelParticipant MTP_channelParticipant(MTPint _user_id, MTPint _inviter_id, MTPint _date); + friend MTPchannelParticipant MTP_channelParticipant(MTPint _user_id, MTPint _date); + friend MTPchannelParticipant MTP_channelParticipantSelf(MTPint _user_id, MTPint _inviter_id, MTPint _date); friend MTPchannelParticipant MTP_channelParticipantModerator(MTPint _user_id, MTPint _inviter_id, MTPint _date); friend MTPchannelParticipant MTP_channelParticipantEditor(MTPint _user_id, MTPint _inviter_id, MTPint _date); friend MTPchannelParticipant MTP_channelParticipantKicked(MTPint _user_id, MTPint _kicked_by, MTPint _date); @@ -9889,16 +9889,6 @@ public: MTPstring vtitle; }; -class MTPDmessageActionChannelToggleComments : public mtpDataImpl { -public: - MTPDmessageActionChannelToggleComments() { - } - MTPDmessageActionChannelToggleComments(MTPBool _enabled) : venabled(_enabled) { - } - - MTPBool venabled; -}; - class MTPDdialog : public mtpDataImpl { public: MTPDdialog() { @@ -12140,18 +12130,22 @@ class MTPDchannelParticipants : public mtpDataImpl { public: MTPDchannelParticipants() { } - MTPDchannelParticipants(MTPint _flags, MTPint _channel_id, const MTPChannelParticipant &_self_participant) : vflags(_flags), vchannel_id(_channel_id), vself_participant(_self_participant) { + MTPDchannelParticipants(MTPint _flags, MTPint _channel_id, MTPint _participants_count, const MTPChannelParticipant &_self_participant, const MTPVector &_participants) : vflags(_flags), vchannel_id(_channel_id), vparticipants_count(_participants_count), vself_participant(_self_participant), vparticipants(_participants) { } MTPint vflags; MTPint vchannel_id; + MTPint vparticipants_count; MTPChannelParticipant vself_participant; + MTPVector vparticipants; enum { flag_self_participant = (1 << 0), + flag_participants = (1 << 1), }; bool has_self_participant() const { return vflags.v & flag_self_participant; } + bool has_participants() const { return vflags.v & flag_participants; } }; class MTPDmessageRange : public mtpDataImpl { @@ -12259,7 +12253,18 @@ class MTPDchannelParticipant : public mtpDataImpl { public: MTPDchannelParticipant() { } - MTPDchannelParticipant(MTPint _user_id, MTPint _inviter_id, MTPint _date) : vuser_id(_user_id), vinviter_id(_inviter_id), vdate(_date) { + MTPDchannelParticipant(MTPint _user_id, MTPint _date) : vuser_id(_user_id), vdate(_date) { + } + + MTPint vuser_id; + MTPint vdate; +}; + +class MTPDchannelParticipantSelf : public mtpDataImpl { +public: + MTPDchannelParticipantSelf() { + } + MTPDchannelParticipantSelf(MTPint _user_id, MTPint _inviter_id, MTPint _date) : vuser_id(_user_id), vinviter_id(_inviter_id), vdate(_date) { } MTPint vuser_id; @@ -21852,10 +21857,6 @@ inline uint32 MTPmessageAction::innerLength() const { const MTPDmessageActionChannelCreate &v(c_messageActionChannelCreate()); return v.vtitle.innerLength(); } - case mtpc_messageActionChannelToggleComments: { - const MTPDmessageActionChannelToggleComments &v(c_messageActionChannelToggleComments()); - return v.venabled.innerLength(); - } } return 0; } @@ -21904,11 +21905,6 @@ inline void MTPmessageAction::read(const mtpPrime *&from, const mtpPrime *end, m MTPDmessageActionChannelCreate &v(_messageActionChannelCreate()); v.vtitle.read(from, end); } break; - case mtpc_messageActionChannelToggleComments: _type = cons; { - if (!data) setData(new MTPDmessageActionChannelToggleComments()); - MTPDmessageActionChannelToggleComments &v(_messageActionChannelToggleComments()); - v.venabled.read(from, end); - } break; default: throw mtpErrorUnexpected(cons, "MTPmessageAction"); } } @@ -21943,10 +21939,6 @@ inline void MTPmessageAction::write(mtpBuffer &to) const { const MTPDmessageActionChannelCreate &v(c_messageActionChannelCreate()); v.vtitle.write(to); } break; - case mtpc_messageActionChannelToggleComments: { - const MTPDmessageActionChannelToggleComments &v(c_messageActionChannelToggleComments()); - v.venabled.write(to); - } break; } } inline MTPmessageAction::MTPmessageAction(mtpTypeId type) : mtpDataOwner(0), _type(type) { @@ -21960,7 +21952,6 @@ inline MTPmessageAction::MTPmessageAction(mtpTypeId type) : mtpDataOwner(0), _ty case mtpc_messageActionChatDeleteUser: setData(new MTPDmessageActionChatDeleteUser()); break; case mtpc_messageActionChatJoinedByLink: setData(new MTPDmessageActionChatJoinedByLink()); break; case mtpc_messageActionChannelCreate: setData(new MTPDmessageActionChannelCreate()); break; - case mtpc_messageActionChannelToggleComments: setData(new MTPDmessageActionChannelToggleComments()); break; default: throw mtpErrorBadTypeId(type, "MTPmessageAction"); } } @@ -21978,8 +21969,6 @@ inline MTPmessageAction::MTPmessageAction(MTPDmessageActionChatJoinedByLink *_da } inline MTPmessageAction::MTPmessageAction(MTPDmessageActionChannelCreate *_data) : mtpDataOwner(_data), _type(mtpc_messageActionChannelCreate) { } -inline MTPmessageAction::MTPmessageAction(MTPDmessageActionChannelToggleComments *_data) : mtpDataOwner(_data), _type(mtpc_messageActionChannelToggleComments) { -} inline MTPmessageAction MTP_messageActionEmpty() { return MTPmessageAction(mtpc_messageActionEmpty); } @@ -22007,9 +21996,6 @@ inline MTPmessageAction MTP_messageActionChatJoinedByLink(MTPint _inviter_id) { inline MTPmessageAction MTP_messageActionChannelCreate(const MTPstring &_title) { return MTPmessageAction(new MTPDmessageActionChannelCreate(_title)); } -inline MTPmessageAction MTP_messageActionChannelToggleComments(MTPBool _enabled) { - return MTPmessageAction(new MTPDmessageActionChannelToggleComments(_enabled)); -} inline uint32 MTPdialog::innerLength() const { switch (_type) { @@ -28018,7 +28004,7 @@ inline MTPchannelParticipants::MTPchannelParticipants() : mtpDataOwner(new MTPDc inline uint32 MTPchannelParticipants::innerLength() const { const MTPDchannelParticipants &v(c_channelParticipants()); - return v.vflags.innerLength() + v.vchannel_id.innerLength() + (v.has_self_participant() ? v.vself_participant.innerLength() : 0); + return v.vflags.innerLength() + v.vchannel_id.innerLength() + v.vparticipants_count.innerLength() + (v.has_self_participant() ? v.vself_participant.innerLength() : 0) + (v.has_participants() ? v.vparticipants.innerLength() : 0); } inline mtpTypeId MTPchannelParticipants::type() const { return mtpc_channelParticipants; @@ -28030,18 +28016,22 @@ inline void MTPchannelParticipants::read(const mtpPrime *&from, const mtpPrime * MTPDchannelParticipants &v(_channelParticipants()); v.vflags.read(from, end); v.vchannel_id.read(from, end); + v.vparticipants_count.read(from, end); if (v.has_self_participant()) { v.vself_participant.read(from, end); } else { v.vself_participant = MTPChannelParticipant(); } + if (v.has_participants()) { v.vparticipants.read(from, end); } else { v.vparticipants = MTPVector(); } } inline void MTPchannelParticipants::write(mtpBuffer &to) const { const MTPDchannelParticipants &v(c_channelParticipants()); v.vflags.write(to); v.vchannel_id.write(to); + v.vparticipants_count.write(to); if (v.has_self_participant()) v.vself_participant.write(to); + if (v.has_participants()) v.vparticipants.write(to); } inline MTPchannelParticipants::MTPchannelParticipants(MTPDchannelParticipants *_data) : mtpDataOwner(_data) { } -inline MTPchannelParticipants MTP_channelParticipants(MTPint _flags, MTPint _channel_id, const MTPChannelParticipant &_self_participant) { - return MTPchannelParticipants(new MTPDchannelParticipants(_flags, _channel_id, _self_participant)); +inline MTPchannelParticipants MTP_channelParticipants(MTPint _flags, MTPint _channel_id, MTPint _participants_count, const MTPChannelParticipant &_self_participant, const MTPVector &_participants) { + return MTPchannelParticipants(new MTPDchannelParticipants(_flags, _channel_id, _participants_count, _self_participant, _participants)); } inline MTPmessageRange::MTPmessageRange() : mtpDataOwner(new MTPDmessageRange()) { @@ -28284,6 +28274,10 @@ inline uint32 MTPchannelParticipant::innerLength() const { switch (_type) { case mtpc_channelParticipant: { const MTPDchannelParticipant &v(c_channelParticipant()); + return v.vuser_id.innerLength() + v.vdate.innerLength(); + } + case mtpc_channelParticipantSelf: { + const MTPDchannelParticipantSelf &v(c_channelParticipantSelf()); return v.vuser_id.innerLength() + v.vinviter_id.innerLength() + v.vdate.innerLength(); } case mtpc_channelParticipantModerator: { @@ -28316,6 +28310,12 @@ inline void MTPchannelParticipant::read(const mtpPrime *&from, const mtpPrime *e if (!data) setData(new MTPDchannelParticipant()); MTPDchannelParticipant &v(_channelParticipant()); v.vuser_id.read(from, end); + v.vdate.read(from, end); + } break; + case mtpc_channelParticipantSelf: _type = cons; { + if (!data) setData(new MTPDchannelParticipantSelf()); + MTPDchannelParticipantSelf &v(_channelParticipantSelf()); + v.vuser_id.read(from, end); v.vinviter_id.read(from, end); v.vdate.read(from, end); } break; @@ -28353,6 +28353,11 @@ inline void MTPchannelParticipant::write(mtpBuffer &to) const { case mtpc_channelParticipant: { const MTPDchannelParticipant &v(c_channelParticipant()); v.vuser_id.write(to); + v.vdate.write(to); + } break; + case mtpc_channelParticipantSelf: { + const MTPDchannelParticipantSelf &v(c_channelParticipantSelf()); + v.vuser_id.write(to); v.vinviter_id.write(to); v.vdate.write(to); } break; @@ -28383,6 +28388,7 @@ inline void MTPchannelParticipant::write(mtpBuffer &to) const { inline MTPchannelParticipant::MTPchannelParticipant(mtpTypeId type) : mtpDataOwner(0), _type(type) { switch (type) { case mtpc_channelParticipant: setData(new MTPDchannelParticipant()); break; + case mtpc_channelParticipantSelf: setData(new MTPDchannelParticipantSelf()); break; case mtpc_channelParticipantModerator: setData(new MTPDchannelParticipantModerator()); break; case mtpc_channelParticipantEditor: setData(new MTPDchannelParticipantEditor()); break; case mtpc_channelParticipantKicked: setData(new MTPDchannelParticipantKicked()); break; @@ -28392,6 +28398,8 @@ inline MTPchannelParticipant::MTPchannelParticipant(mtpTypeId type) : mtpDataOwn } inline MTPchannelParticipant::MTPchannelParticipant(MTPDchannelParticipant *_data) : mtpDataOwner(_data), _type(mtpc_channelParticipant) { } +inline MTPchannelParticipant::MTPchannelParticipant(MTPDchannelParticipantSelf *_data) : mtpDataOwner(_data), _type(mtpc_channelParticipantSelf) { +} inline MTPchannelParticipant::MTPchannelParticipant(MTPDchannelParticipantModerator *_data) : mtpDataOwner(_data), _type(mtpc_channelParticipantModerator) { } inline MTPchannelParticipant::MTPchannelParticipant(MTPDchannelParticipantEditor *_data) : mtpDataOwner(_data), _type(mtpc_channelParticipantEditor) { @@ -28400,8 +28408,11 @@ inline MTPchannelParticipant::MTPchannelParticipant(MTPDchannelParticipantKicked } inline MTPchannelParticipant::MTPchannelParticipant(MTPDchannelParticipantCreator *_data) : mtpDataOwner(_data), _type(mtpc_channelParticipantCreator) { } -inline MTPchannelParticipant MTP_channelParticipant(MTPint _user_id, MTPint _inviter_id, MTPint _date) { - return MTPchannelParticipant(new MTPDchannelParticipant(_user_id, _inviter_id, _date)); +inline MTPchannelParticipant MTP_channelParticipant(MTPint _user_id, MTPint _date) { + return MTPchannelParticipant(new MTPDchannelParticipant(_user_id, _date)); +} +inline MTPchannelParticipant MTP_channelParticipantSelf(MTPint _user_id, MTPint _inviter_id, MTPint _date) { + return MTPchannelParticipant(new MTPDchannelParticipantSelf(_user_id, _inviter_id, _date)); } inline MTPchannelParticipant MTP_channelParticipantModerator(MTPint _user_id, MTPint _inviter_id, MTPint _date) { return MTPchannelParticipant(new MTPDchannelParticipantModerator(_user_id, _inviter_id, _date)); diff --git a/Telegram/SourceFiles/mtproto/scheme.tl b/Telegram/SourceFiles/mtproto/scheme.tl index aec1ab483..12f2095b7 100644 --- a/Telegram/SourceFiles/mtproto/scheme.tl +++ b/Telegram/SourceFiles/mtproto/scheme.tl @@ -256,7 +256,6 @@ messageActionChatAddUser#5e3cfc4b user_id:int = MessageAction; messageActionChatDeleteUser#b2ae9b0c user_id:int = MessageAction; messageActionChatJoinedByLink#f89cf5e8 inviter_id:int = MessageAction; messageActionChannelCreate#95d2ac92 title:string = MessageAction; -messageActionChannelToggleComments#f2863903 enabled:Bool = MessageAction; dialog#c1dd804a peer:Peer top_message:int read_inbox_max_id:int unread_count:int notify_settings:PeerNotifySettings = Dialog; dialogChannel#5b8496b2 peer:Peer top_message:int top_important_message:int read_inbox_max_id:int unread_count:int unread_important_count:int notify_settings:PeerNotifySettings pts:int = Dialog; @@ -588,7 +587,7 @@ inputChannel#afeb712e channel_id:int access_hash:long = InputChannel; contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector users:Vector = contacts.ResolvedPeer; -channelParticipants#57d3e762 flags:# channel_id:int self_participant:flags.0?ChannelParticipant = ChannelParticipants; +channelParticipants#dee6d213 flags:# channel_id:int participants_count:int self_participant:flags.0?ChannelParticipant participants:flags.1?Vector = ChannelParticipants; messageRange#ae30253 min_id:int max_id:int = MessageRange; @@ -602,7 +601,8 @@ channelMessagesFilterEmpty#94d42ee7 = ChannelMessagesFilter; channelMessagesFilter#cd77d957 flags:# ranges:Vector = ChannelMessagesFilter; channelMessagesFilterCollapsed#fa01232e = ChannelMessagesFilter; -channelParticipant#506116ea user_id:int inviter_id:int date:int = ChannelParticipant; +channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant; +channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant; channelParticipantModerator#91057fef user_id:int inviter_id:int date:int = ChannelParticipant; channelParticipantEditor#98192d61 user_id:int inviter_id:int date:int = ChannelParticipant; channelParticipantKicked#8cc5e69a user_id:int kicked_by:int date:int = ChannelParticipant; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 125ad8c04..8c46ef23f 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -271,11 +271,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 = _hist->overview[_type].size(); + if (current < 0 || current >= l || _hist->overview[_type][current] != msgId) { current = -1; for (int32 i = 0; i < l; ++i) { - if (_hist->_overview[_type][i] == msgId) { + if (_hist->overview[_type][i] == msgId) { current = i; break; } @@ -328,7 +328,10 @@ void OverviewInner::searchReceived(bool fromStart, const MTPmessages_Messages &r if (_peer && _peer->isChannel()) { _peer->asChannel()->ptsReceived(d.vpts.v); } else { - LOG(("App Error: received messages.channelMessages in OverviewInner::searchReceived when no channel was passed!")); + LOG(("API Error: received messages.channelMessages when no channel was passed! (OverviewInner::searchReceived)")); + } + if (d.has_collapsed()) { // should not be returned + LOG(("API Error: channels.getMessages and messages.getMessages should not return collapsed groups! (OverviewInner::searchReceived)")); } App::feedUsers(d.vusers); @@ -347,7 +350,7 @@ void OverviewInner::searchReceived(bool fromStart, const MTPmessages_Messages &r _itemsToBeLoaded = LinksOverviewPerPage * 2; } for (QVector::const_iterator i = messages->cbegin(), e = messages->cend(); i != e; ++i) { - HistoryItem *item = App::histories().addToBack(*i, -1); + HistoryItem *item = App::histories().addNewMessage(*i, -1); _searchResults.push_front(item->id); _lastSearchId = item->id; } @@ -454,11 +457,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 >= _hist->overview[_type].size()) { msgId = 0; index = -1; } else { - msgId = _hist->_overview[_type][index]; + msgId = _hist->overview[_type][index]; } } else { while (index >= 0 && index < _items.size() && !_items[index].msgid) { @@ -867,7 +870,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) ? _hist->overview[_type][i] : _items[i].msgid; if (!msgid) continue; SelectedItems::iterator j = _selected.find(msgid); @@ -886,7 +889,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) ? _hist->overview[_type][i] : _items[i].msgid; if (!msgid) continue; SelectedItems::iterator j = _selected.find(msgid); @@ -933,7 +936,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 = _hist->overview[_type].indexOf(msgId.msg); if (index >= 0) { return _addToY + int32(index * _audioHeight); } @@ -957,7 +960,7 @@ void OverviewInner::preloadMore() { bool OverviewInner::preloadLocal() { if (_type != OverviewLinks) return false; - if (_itemsToBeLoaded >= _hist->_overview[_type].size()) return false; + if (_itemsToBeLoaded >= _hist->overview[_type].size()) return false; _itemsToBeLoaded += LinksOverviewPerPage; mediaOverviewUpdated(); return true; @@ -990,7 +993,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); p.setClipRect(r); - if (_hist->_overview[_type].isEmpty()) { + if (_hist->overview[_type].isEmpty()) { QPoint dogPos((_width - st::msgDogImg.pxWidth()) / 2, ((height() - st::msgDogImg.pxHeight()) * 4) / 9); p.drawPixmap(dogPos, *cChatDogImage()); return; @@ -1013,7 +1016,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { if (_type == OverviewPhotos) { int32 rowFrom = int32(r.top() - _addToY - st::overviewPhotoSkip) / int32(_vsize + st::overviewPhotoSkip); int32 rowTo = int32(r.bottom() - _addToY - st::overviewPhotoSkip) / int32(_vsize + st::overviewPhotoSkip) + 1; - History::MediaOverview &overview(_hist->_overview[_type]); + History::MediaOverview &overview(_hist->overview[_type]); int32 count = overview.size(); float64 w = float64(_width - st::overviewPhotoSkip) / _photosInRow; for (int32 row = rowFrom; row < rowTo; ++row) { @@ -1096,7 +1099,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { } else if (_type == OverviewAudioDocuments) { int32 from = int32(r.top() - _addToY) / int32(_audioHeight); int32 to = int32(r.bottom() - _addToY) / int32(_audioHeight) + 1; - History::MediaOverview &overview(_hist->_overview[_type]); + History::MediaOverview &overview(_hist->overview[_type]); int32 count = overview.size(); p.translate(_audioLeft, _addToY + from * _audioHeight); for (int32 index = from; index < to; ++index) { @@ -1299,7 +1302,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 = _hist->overview[_type].size(); if (i < 0) { i = 0; upon = false; @@ -1309,7 +1312,7 @@ void OverviewInner::onUpdateSelected() { upon = false; } if (i >= 0) { - MsgId msgid = _hist->_overview[_type][i]; + MsgId msgid = _hist->overview[_type][i]; HistoryItem *histItem = App::histItemById(_channel, msgid); if (histItem) { item = histItem; @@ -1325,7 +1328,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 = _hist->overview[_type].size(); bool upon = true; if (m.y() < _addToY) { @@ -1337,7 +1340,7 @@ void OverviewInner::onUpdateSelected() { upon = false; } if (i >= 0) { - MsgId msgid = _hist->_overview[_type][i]; + MsgId msgid = _hist->overview[_type][i]; HistoryItem *histItem = App::histItemById(_channel, msgid); if (histItem) { item = histItem; @@ -1941,14 +1944,14 @@ void OverviewInner::goToMessage() { void OverviewInner::forwardMessage() { HistoryItem *item = App::contextItem(); - if (!item || item->itemType() != HistoryItem::MsgType) return; + if (!item || item->type() != HistoryItemMsg || item->serviceMsg()) return; App::main()->forwardLayer(); } void OverviewInner::deleteMessage() { HistoryItem *item = App::contextItem(); - if (!item || item->itemType() != HistoryItem::MsgType) return; + if (!item || item->type() != HistoryItemMsg) return; HistoryMessage *msg = item->toHistoryMessage(); App::main()->deleteLayer((msg && msg->uploading()) ? -2 : -1); @@ -1956,7 +1959,7 @@ void OverviewInner::deleteMessage() { void OverviewInner::selectMessage() { HistoryItem *item = App::contextItem(); - if (!item || item->itemType() != HistoryItem::MsgType) return; + if (!item || item->type() != HistoryItemMsg || item->serviceMsg()) return; if (!_selected.isEmpty() && _selected.cbegin().value() != FullItemSel) { _selected.clear(); @@ -2153,7 +2156,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 : _hist->overview[_type]); int32 l = o.size(), tocheck = qMin(l, _itemsToBeLoaded); _items.reserve(2 * l); // day items @@ -2231,7 +2234,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(_hist->overview[_type]); int32 l = o.size(); _items.reserve(2 * l); // day items @@ -2444,9 +2447,9 @@ void OverviewInner::itemResized(HistoryItem *item, bool scrollToIt) { void OverviewInner::msgUpdated(const HistoryItem *msg) { if (!msg || _hist != msg->history()) return; MsgId msgid = msg->id; - if (_hist->_overviewIds[_type].constFind(msgid) != _hist->_overviewIds[_type].cend()) { + if (_hist->overviewIds[_type].constFind(msgid) != _hist->overviewIds[_type].cend()) { if (_type == OverviewPhotos) { - int32 index = _hist->_overview[_type].indexOf(msgid); + int32 index = _hist->overview[_type].indexOf(msgid); if (index >= 0) { float64 w = (float64(width() - st::overviewPhotoSkip) / _photosInRow); int32 vsize = (_vsize + st::overviewPhotoSkip); @@ -2454,7 +2457,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 = _hist->overview[_type].indexOf(msgid); if (index >= 0) { update(_audioLeft, _addToY + int32(index * _audioHeight), _audioWidth, _audioHeight); } @@ -2481,7 +2484,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 = _hist->overview[_type].size(), fullCount = _hist->overviewCount[_type]; if (fullCount > 0) { int32 cnt = count - (fullCount % _photosInRow); if (cnt < 0) cnt += _photosInRow; @@ -2493,7 +2496,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 = _hist->overview[_type].size(), fullCount = _hist->overviewCount[_type]; newHeight = _height = count * _audioHeight + 2 * st::playlistPadding; _addToY = st::playlistPadding; } else if (_type == OverviewLinks) { @@ -2947,10 +2950,6 @@ void OverviewWidget::onDeleteSelectedSure() { } } - if (!ids.isEmpty()) { - App::main()->deleteMessages(peer(), ids); - } - onClearSelected(); for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) { i.value()->destroy(); @@ -2959,22 +2958,34 @@ void OverviewWidget::onDeleteSelectedSure() { App::main()->itemResized(0); } App::wnd()->hideLayer(); + + if (!ids.isEmpty()) { + App::main()->deleteMessages(peer(), ids); + } } void OverviewWidget::onDeleteContextSure() { HistoryItem *item = App::contextItem(); - if (!item || item->itemType() != HistoryItem::MsgType) { + if (!item || item->type() != HistoryItemMsg) { return; } - if (item->id > 0) { - App::main()->deleteMessages(item->history()->peer, QVector(1, MTP_int(item->id))); - } + QVector toDelete(1, MTP_int(item->id)); + History *h = item->history(); + bool wasOnServer = (item->id > 0), wasLast = (h->lastMsg == item); item->destroy(); + if (!wasOnServer && wasLast && !h->lastMsg) { + App::main()->checkPeerHistory(h->peer); + } + if (App::main() && App::main()->peer() == peer()) { App::main()->itemResized(0); } App::wnd()->hideLayer(); + + if (wasOnServer) { + App::main()->deleteMessages(h->peer, toDelete); + } } void OverviewWidget::onClearSelected() { diff --git a/Telegram/SourceFiles/playerwidget.cpp b/Telegram/SourceFiles/playerwidget.cpp index b398de20e..273e44454 100644 --- a/Telegram/SourceFiles/playerwidget.cpp +++ b/Telegram/SourceFiles/playerwidget.cpp @@ -246,12 +246,12 @@ void PlayerWidget::updateOverRect(OverState state) { void PlayerWidget::updateControls() { _fullAvailable = (_index >= 0); _prevAvailable = _fullAvailable && (_index > 0); - _nextAvailable = _fullAvailable && (_index < _history->_overview[OverviewAudioDocuments].size() - 1); + _nextAvailable = _fullAvailable && (_index < _history->overview[OverviewAudioDocuments].size() - 1); resizeEvent(0); update(); if (_index >= 0 && _index < MediaOverviewStartPerPage) { - if (_history->_overviewCount[OverviewAudioDocuments] < 0 || _history->_overviewCount[OverviewAudioDocuments] > 0) { + if (_history->overviewCount[OverviewAudioDocuments] < 0 || _history->overviewCount[OverviewAudioDocuments] > 0) { if (App::main()) App::main()->loadMediaBack(_history->peer, OverviewAudioDocuments); } } @@ -261,7 +261,7 @@ void PlayerWidget::findCurrent() { _index = -1; if (!_history) return; - const History::MediaOverview *o = &_history->_overview[OverviewAudioDocuments]; + const History::MediaOverview *o = &_history->overview[OverviewAudioDocuments]; if (_history->channelId() == _song.msgId.channel) { for (int i = 0, l = o->size(); i < l; ++i) { if (o->at(i) == _song.msgId.msg) { @@ -307,8 +307,8 @@ void PlayerWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) if (_history && _history->peer == peer && type == OverviewAudioDocuments) { _index = -1; if (_history->channelId() == _song.msgId.channel) { - for (int i = 0, l = _history->_overview[OverviewAudioDocuments].size(); i < l; ++i) { - if (_history->_overview[OverviewAudioDocuments].at(i) == _song.msgId.msg) { + for (int i = 0, l = _history->overview[OverviewAudioDocuments].size(); i < l; ++i) { + if (_history->overview[OverviewAudioDocuments].at(i) == _song.msgId.msg) { _index = i; break; } @@ -464,7 +464,7 @@ void PlayerWidget::playPausePressed() { void PlayerWidget::prevPressed() { if (isHidden()) return; - const History::MediaOverview *o = _history ? &_history->_overview[OverviewAudioDocuments] : 0; + const History::MediaOverview *o = _history ? &_history->overview[OverviewAudioDocuments] : 0; if (audioPlayer() && o && _index > 0 && _index <= o->size() && !o->isEmpty()) { startPlay(FullMsgId(_history->channelId(), o->at(_index - 1))); } @@ -473,7 +473,7 @@ void PlayerWidget::prevPressed() { void PlayerWidget::nextPressed() { if (isHidden()) return; - const History::MediaOverview *o = _history ? &_history->_overview[OverviewAudioDocuments] : 0; + const History::MediaOverview *o = _history ? &_history->overview[OverviewAudioDocuments] : 0; if (audioPlayer() && o && _index >= 0 && _index < o->size() - 1) { startPlay(FullMsgId(_history->channelId(), o->at(_index + 1))); } diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index 5be6c6e5e..4638338d1 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -913,7 +913,7 @@ bool ProfileInner::updateMediaLinks(int32 *addToScroll) { int32 addToY = _mediaButtons[i]->height() + st::setLittleSkip; - int32 count = (_hist->_overviewCount[i] > 0) ? _hist->_overviewCount[i] : (_hist->_overviewCount[i] == 0 ? _hist->_overview[i].size() : -1); + int32 count = (_hist->overviewCount[i] > 0) ? _hist->overviewCount[i] : (_hist->overviewCount[i] == 0 ? _hist->overview[i].size() : -1); if (count > 0) { _mediaButtons[i]->setText(overviewLinkText(i, count)); if (_mediaButtons[i]->isHidden()) { diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index c8a315278..9c05915c3 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -902,6 +902,12 @@ void MessageLink::onClick(Qt::MouseButton button) const { } } +void CommentsLink::onClick(Qt::MouseButton button) const { + if (button == Qt::LeftButton && App::main() && _item->history()->isChannel()) { + App::main()->showPeerHistory(_item->history()->peer->id, _item->id); + } +} + MsgId clientMsgId() { static MsgId current = -2000000000; return ++current; diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index a87087858..e679a6098 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -127,6 +127,7 @@ inline bool operator<(const FullMsgId &a, const FullMsgId &b) { } static const MsgId ShowAtTheEndMsgId = -0x40000000; +static const MsgId SwitchAtTopMsgId = -0x3FFFFFFF; static const MsgId ShowAtUnreadMsgId = 0; struct NotifySettings { diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index db2db5109..af3ff52d5 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -28,6 +28,8 @@ typedef quint32 uint32; typedef qint64 int64; typedef quint64 uint64; +static const int32 ScrollMax = INT_MAX; + #ifdef Q_OS_WIN typedef float float32; typedef double float64;