mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Load albums of last chat messages.
This commit is contained in:
parent
576883ddc8
commit
730412fefe
14 changed files with 253 additions and 122 deletions
|
@ -560,7 +560,8 @@ void ApiWrap::resolveMessageDatas() {
|
|||
)).done([=](
|
||||
const MTPmessages_Messages &result,
|
||||
mtpRequestId requestId) {
|
||||
gotMessageDatas(nullptr, result, requestId);
|
||||
_session->data().processExistingMessages(nullptr, result);
|
||||
finalizeMessageDataRequest(nullptr, requestId);
|
||||
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
|
||||
finalizeMessageDataRequest(nullptr, requestId);
|
||||
}).afterDelay(kSmallDelayMs).send();
|
||||
|
@ -586,7 +587,8 @@ void ApiWrap::resolveMessageDatas() {
|
|||
)).done([=](
|
||||
const MTPmessages_Messages &result,
|
||||
mtpRequestId requestId) {
|
||||
gotMessageDatas(channel, result, requestId);
|
||||
_session->data().processExistingMessages(channel, result);
|
||||
finalizeMessageDataRequest(channel, requestId);
|
||||
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
|
||||
finalizeMessageDataRequest(channel, requestId);
|
||||
}).afterDelay(kSmallDelayMs).send();
|
||||
|
@ -602,37 +604,6 @@ void ApiWrap::resolveMessageDatas() {
|
|||
}
|
||||
}
|
||||
|
||||
void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId requestId) {
|
||||
const auto handleResult = [&](auto &&result) {
|
||||
_session->data().processUsers(result.vusers());
|
||||
_session->data().processChats(result.vchats());
|
||||
_session->data().processMessages(
|
||||
result.vmessages(),
|
||||
NewMessageType::Existing);
|
||||
};
|
||||
switch (msgs.type()) {
|
||||
case mtpc_messages_messages:
|
||||
handleResult(msgs.c_messages_messages());
|
||||
break;
|
||||
case mtpc_messages_messagesSlice:
|
||||
handleResult(msgs.c_messages_messagesSlice());
|
||||
break;
|
||||
case mtpc_messages_channelMessages: {
|
||||
auto &d = msgs.c_messages_channelMessages();
|
||||
if (channel) {
|
||||
channel->ptsReceived(d.vpts().v);
|
||||
} else {
|
||||
LOG(("App Error: received messages.channelMessages when no channel was passed! (ApiWrap::gotDependencyItem)"));
|
||||
}
|
||||
handleResult(d);
|
||||
} break;
|
||||
case mtpc_messages_messagesNotModified:
|
||||
LOG(("API Error: received messages.messagesNotModified! (ApiWrap::gotDependencyItem)"));
|
||||
break;
|
||||
}
|
||||
finalizeMessageDataRequest(channel, requestId);
|
||||
}
|
||||
|
||||
void ApiWrap::finalizeMessageDataRequest(
|
||||
ChannelData *channel,
|
||||
mtpRequestId requestId) {
|
||||
|
|
|
@ -458,7 +458,6 @@ private:
|
|||
void saveDraftsToCloud();
|
||||
|
||||
void resolveMessageDatas();
|
||||
void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId requestId);
|
||||
void finalizeMessageDataRequest(
|
||||
ChannelData *channel,
|
||||
mtpRequestId requestId);
|
||||
|
|
|
@ -438,6 +438,49 @@ void Histories::requestFakeChatListMessage(
|
|||
});
|
||||
}
|
||||
|
||||
void Histories::requestGroupAround(not_null<HistoryItem*> item) {
|
||||
const auto history = item->history();
|
||||
const auto id = item->id;
|
||||
const auto i = _chatListGroupRequests.find(history);
|
||||
if (i != end(_chatListGroupRequests)) {
|
||||
if (i->second.aroundId == id) {
|
||||
return;
|
||||
} else {
|
||||
cancelRequest(i->second.requestId);
|
||||
_chatListGroupRequests.erase(i);
|
||||
}
|
||||
}
|
||||
constexpr auto kMaxAlbumCount = 10;
|
||||
const auto requestId = sendRequest(history, RequestType::History, [=](
|
||||
Fn<void()> finish) {
|
||||
return session().api().request(MTPmessages_GetHistory(
|
||||
history->peer->input,
|
||||
MTP_int(id),
|
||||
MTP_int(0), // offset_date
|
||||
MTP_int(-kMaxAlbumCount),
|
||||
MTP_int(2 * kMaxAlbumCount - 1),
|
||||
MTP_int(0), // max_id
|
||||
MTP_int(0), // min_id
|
||||
MTP_long(0) // hash
|
||||
)).done([=](const MTPmessages_Messages &result) {
|
||||
_owner->processExistingMessages(
|
||||
history->peer->asChannel(),
|
||||
result);
|
||||
_chatListGroupRequests.remove(history);
|
||||
history->migrateToOrMe()->applyChatListGroup(
|
||||
history->channelId(),
|
||||
result);
|
||||
finish();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_chatListGroupRequests.remove(history);
|
||||
finish();
|
||||
}).send();
|
||||
});
|
||||
_chatListGroupRequests.emplace(
|
||||
history,
|
||||
ChatListGroupRequest{ .aroundId = id, .requestId = requestId });
|
||||
}
|
||||
|
||||
void Histories::sendPendingReadInbox(not_null<History*> history) {
|
||||
if (const auto state = lookup(history)) {
|
||||
DEBUG_LOG(("Reading: send pending now with till %1 and when %2"
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
void changeDialogUnreadMark(not_null<History*> history, bool unread);
|
||||
void requestFakeChatListMessage(not_null<History*> history);
|
||||
|
||||
void requestGroupAround(not_null<HistoryItem*> item);
|
||||
|
||||
void deleteMessages(
|
||||
not_null<History*> history,
|
||||
const QVector<MTPint> &ids,
|
||||
|
@ -95,6 +97,10 @@ private:
|
|||
bool sentReadDone = false;
|
||||
bool postponedRequestEntry = false;
|
||||
};
|
||||
struct ChatListGroupRequest {
|
||||
MsgId aroundId = 0;
|
||||
mtpRequestId requestId = 0;
|
||||
};
|
||||
|
||||
void readInboxTill(not_null<History*> history, MsgId tillId, bool force);
|
||||
void sendReadRequests();
|
||||
|
@ -130,6 +136,10 @@ private:
|
|||
|
||||
base::flat_set<not_null<History*>> _fakeChatListRequests;
|
||||
|
||||
base::flat_map<
|
||||
not_null<History*>,
|
||||
ChatListGroupRequest> _chatListGroupRequests;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -63,6 +63,7 @@ constexpr auto kFastRevokeRestriction = 24 * 60 * TimeId(60);
|
|||
constexpr auto kMaxPreviewImages = 3;
|
||||
|
||||
using ItemPreview = HistoryView::ItemPreview;
|
||||
using ItemPreviewImage = HistoryView::ItemPreviewImage;
|
||||
|
||||
[[nodiscard]] Call ComputeCallData(const MTPDmessageActionPhoneCall &call) {
|
||||
auto result = Call();
|
||||
|
@ -191,25 +192,18 @@ using ItemPreview = HistoryView::ItemPreview;
|
|||
return square;
|
||||
}
|
||||
|
||||
struct PreparedPreview {
|
||||
QImage preview;
|
||||
bool loading = false;
|
||||
|
||||
explicit operator bool() const {
|
||||
return !preview.isNull() || loading;
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] PreparedPreview PreparePhotoPreview(
|
||||
[[nodiscard]] ItemPreviewImage PreparePhotoPreview(
|
||||
not_null<const HistoryItem*> item,
|
||||
const std::shared_ptr<PhotoMedia> &media,
|
||||
ImageRoundRadius radius) {
|
||||
const auto photo = media->owner();
|
||||
const auto readyCacheKey = reinterpret_cast<uint64>(photo.get());
|
||||
if (const auto small = media->image(PhotoSize::Small)) {
|
||||
return { PreparePreviewImage(small, radius) };
|
||||
return { PreparePreviewImage(small, radius), readyCacheKey };
|
||||
} else if (const auto thumbnail = media->image(PhotoSize::Thumbnail)) {
|
||||
return { PreparePreviewImage(thumbnail, radius) };
|
||||
return { PreparePreviewImage(thumbnail, radius), readyCacheKey };
|
||||
} else if (const auto large = media->image(PhotoSize::Large)) {
|
||||
return { PreparePreviewImage(large, radius) };
|
||||
return { PreparePreviewImage(large, radius), readyCacheKey };
|
||||
}
|
||||
const auto allowedToDownload = [&] {
|
||||
const auto photo = media->owner();
|
||||
|
@ -223,29 +217,32 @@ struct PreparedPreview {
|
|||
item->history()->peer,
|
||||
photo);
|
||||
}();
|
||||
const auto cacheKey = allowedToDownload ? 0 : readyCacheKey;
|
||||
if (allowedToDownload) {
|
||||
media->owner()->load(PhotoSize::Small, item->fullId());
|
||||
}
|
||||
if (const auto blurred = media->thumbnailInline()) {
|
||||
return { PreparePreviewImage(blurred, radius), allowedToDownload };
|
||||
return { PreparePreviewImage(blurred, radius), cacheKey };
|
||||
}
|
||||
return { QImage(), allowedToDownload };
|
||||
return { QImage(), allowedToDownload ? 0 : cacheKey };
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparedPreview PrepareFilePreviewImage(
|
||||
[[nodiscard]] ItemPreviewImage PrepareFilePreviewImage(
|
||||
not_null<const HistoryItem*> item,
|
||||
const std::shared_ptr<DocumentMedia> &media,
|
||||
ImageRoundRadius radius) {
|
||||
Expects(media->owner()->hasThumbnail());
|
||||
|
||||
const auto document = media->owner();
|
||||
const auto readyCacheKey = reinterpret_cast<uint64>(document.get());
|
||||
if (const auto thumbnail = media->thumbnail()) {
|
||||
return { PreparePreviewImage(thumbnail, radius) };
|
||||
return { PreparePreviewImage(thumbnail, radius), readyCacheKey };
|
||||
}
|
||||
media->owner()->loadThumbnail(item->fullId());
|
||||
document->loadThumbnail(item->fullId());
|
||||
if (const auto blurred = media->thumbnailInline()) {
|
||||
return { PreparePreviewImage(blurred, radius), true };
|
||||
return { PreparePreviewImage(blurred, radius), 0 };
|
||||
}
|
||||
return { QImage(), true };
|
||||
return { QImage(), 0 };
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage PutPlayIcon(QImage preview) {
|
||||
|
@ -260,28 +257,17 @@ struct PreparedPreview {
|
|||
return preview;
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparedPreview PrepareFilePreview(
|
||||
[[nodiscard]] ItemPreviewImage PrepareFilePreview(
|
||||
not_null<const HistoryItem*> item,
|
||||
const std::shared_ptr<DocumentMedia> &media,
|
||||
ImageRoundRadius radius) {
|
||||
if (auto result = PrepareFilePreviewImage(item, media, radius)) {
|
||||
const auto document = media->owner();
|
||||
if (!result.preview.isNull()
|
||||
&& (document->isVideoFile() || document->isVideoMessage())) {
|
||||
result.preview = PutPlayIcon(std::move(result.preview));
|
||||
}
|
||||
return result;
|
||||
auto result = PrepareFilePreviewImage(item, media, radius);
|
||||
const auto document = media->owner();
|
||||
if (!result.data.isNull()
|
||||
&& (document->isVideoFile() || document->isVideoMessage())) {
|
||||
result.data = PutPlayIcon(std::move(result.data));
|
||||
}
|
||||
Expects(media->owner()->hasThumbnail());
|
||||
|
||||
if (const auto thumbnail = media->thumbnail()) {
|
||||
return { PreparePreviewImage(thumbnail, radius) };
|
||||
}
|
||||
media->owner()->loadThumbnail(item->fullId());
|
||||
if (const auto blurred = media->thumbnailInline()) {
|
||||
return { PreparePreviewImage(blurred, radius), true };
|
||||
}
|
||||
return { QImage(), true };
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool TryFilePreview(not_null<DocumentData*> document) {
|
||||
|
@ -290,6 +276,20 @@ struct PreparedPreview {
|
|||
&& !document->isAudioFile();
|
||||
}
|
||||
|
||||
template <typename MediaType>
|
||||
[[nodiscard]] ItemPreviewImage FindCachedPreview(
|
||||
const std::vector<ItemPreviewImage> *existing,
|
||||
not_null<MediaType*> data) {
|
||||
if (!existing) {
|
||||
return {};
|
||||
}
|
||||
const auto i = ranges::find(
|
||||
*existing,
|
||||
reinterpret_cast<uint64>(data.get()),
|
||||
&ItemPreviewImage::cacheKey);
|
||||
return (i != end(*existing)) ? *i : ItemPreviewImage();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TextForMimeData WithCaptionClipboardText(
|
||||
|
@ -554,22 +554,27 @@ ItemPreview MediaPhoto::toPreview(ToPreviewOptions options) const {
|
|||
return toGroupPreview(group->items, options);
|
||||
}
|
||||
}
|
||||
const auto caption = options.hideCaption
|
||||
? QString()
|
||||
: parent()->originalText().text;
|
||||
auto images = std::vector<QImage>();
|
||||
const auto media = _photo->createMediaView();
|
||||
const auto radius = _chat
|
||||
? ImageRoundRadius::Ellipse
|
||||
: ImageRoundRadius::Small;
|
||||
auto images = std::vector<ItemPreviewImage>();
|
||||
auto context = std::any();
|
||||
if (auto prepared = PreparePhotoPreview(parent(), media, radius)) {
|
||||
images.push_back(std::move(prepared.preview));
|
||||
if (prepared.loading) {
|
||||
context = media;
|
||||
if (auto cached = FindCachedPreview(options.existing, _photo)) {
|
||||
images.push_back(std::move(cached));
|
||||
} else {
|
||||
const auto media = _photo->createMediaView();
|
||||
const auto radius = _chat
|
||||
? ImageRoundRadius::Ellipse
|
||||
: ImageRoundRadius::Small;
|
||||
if (auto prepared = PreparePhotoPreview(parent(), media, radius)
|
||||
; prepared || !prepared.cacheKey) {
|
||||
images.push_back(std::move(prepared));
|
||||
if (!prepared.cacheKey) {
|
||||
context = media;
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto type = tr::lng_in_dlg_photo(tr::now);
|
||||
const auto caption = options.hideCaption
|
||||
? QString()
|
||||
: parent()->originalText().text;
|
||||
return {
|
||||
.text = WithCaptionDialogsText(type, caption, !images.empty()),
|
||||
.images = std::move(images),
|
||||
|
@ -751,6 +756,23 @@ ItemPreview MediaFile::toPreview(ToPreviewOptions options) const {
|
|||
if (const auto sticker = _document->sticker()) {
|
||||
return Media::toPreview(options);
|
||||
}
|
||||
auto images = std::vector<ItemPreviewImage>();
|
||||
auto context = std::any();
|
||||
if (auto cached = FindCachedPreview(options.existing, _document)) {
|
||||
images.push_back(std::move(cached));
|
||||
} else if (TryFilePreview(_document)) {
|
||||
const auto media = _document->createMediaView();
|
||||
const auto radius = _document->isVideoMessage()
|
||||
? ImageRoundRadius::Ellipse
|
||||
: ImageRoundRadius::Small;
|
||||
if (auto prepared = PrepareFilePreview(parent(), media, radius)
|
||||
; prepared || !prepared.cacheKey) {
|
||||
images.push_back(std::move(prepared));
|
||||
if (!prepared.cacheKey) {
|
||||
context = media;
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto type = [&] {
|
||||
using namespace Ui::Text;
|
||||
if (_document->isVideoMessage()) {
|
||||
|
@ -772,22 +794,6 @@ ItemPreview MediaFile::toPreview(ToPreviewOptions options) const {
|
|||
const auto caption = options.hideCaption
|
||||
? QString()
|
||||
: parent()->originalText().text;
|
||||
auto images = std::vector<QImage>();
|
||||
const auto media = TryFilePreview(_document)
|
||||
? _document->createMediaView()
|
||||
: nullptr;
|
||||
const auto radius = _document->isVideoMessage()
|
||||
? ImageRoundRadius::Ellipse
|
||||
: ImageRoundRadius::Small;
|
||||
auto context = std::any();
|
||||
if (media) {
|
||||
if (auto prepared = PrepareFilePreview(parent(), media, radius)) {
|
||||
images.push_back(std::move(prepared.preview));
|
||||
if (prepared.loading) {
|
||||
context = media;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
.text = WithCaptionDialogsText(type, caption, !images.empty()),
|
||||
.images = std::move(images),
|
||||
|
|
|
@ -28,6 +28,7 @@ enum class Context : char;
|
|||
class Element;
|
||||
class Media;
|
||||
struct ItemPreview;
|
||||
struct ItemPreviewImage;
|
||||
struct ToPreviewOptions;
|
||||
} // namespace HistoryView
|
||||
|
||||
|
@ -75,6 +76,7 @@ public:
|
|||
not_null<HistoryItem*> parent() const;
|
||||
|
||||
using ToPreviewOptions = HistoryView::ToPreviewOptions;
|
||||
using ItemPreviewImage = HistoryView::ItemPreviewImage;
|
||||
using ItemPreview = HistoryView::ItemPreview;
|
||||
|
||||
virtual std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) = 0;
|
||||
|
|
|
@ -1859,6 +1859,26 @@ void Session::processMessages(
|
|||
processMessages(data.v, type);
|
||||
}
|
||||
|
||||
void Session::processExistingMessages(
|
||||
ChannelData *channel,
|
||||
const MTPmessages_Messages &data) {
|
||||
data.match([&](const MTPDmessages_channelMessages &data) {
|
||||
if (channel) {
|
||||
channel->ptsReceived(data.vpts().v);
|
||||
} else {
|
||||
LOG(("App Error: received messages.channelMessages!"));
|
||||
}
|
||||
}, [](const auto &) {});
|
||||
|
||||
data.match([&](const MTPDmessages_messagesNotModified&) {
|
||||
LOG(("API Error: received messages.messagesNotModified!"));
|
||||
}, [&](const auto &data) {
|
||||
processUsers(data.vusers());
|
||||
processChats(data.vchats());
|
||||
processMessages(data.vmessages(), NewMessageType::Existing);
|
||||
});
|
||||
}
|
||||
|
||||
const Session::Messages *Session::messagesList(ChannelId channelId) const {
|
||||
if (channelId == NoChannel) {
|
||||
return &_messages;
|
||||
|
|
|
@ -341,6 +341,9 @@ public:
|
|||
void processMessages(
|
||||
const MTPVector<MTPMessage> &data,
|
||||
NewMessageType type);
|
||||
void processExistingMessages(
|
||||
ChannelData *channel,
|
||||
const MTPmessages_Messages &data);
|
||||
void processMessagesDeleted(
|
||||
ChannelId channelId,
|
||||
const QVector<MTPint> &data);
|
||||
|
|
|
@ -105,6 +105,7 @@ void MessageView::paint(
|
|||
return;
|
||||
}
|
||||
if (_textCachedFor != item.get()) {
|
||||
options.existing = &_imagesCache;
|
||||
auto preview = item->toPreview(options);
|
||||
if (!preview.images.empty() && preview.imagesInTextPosition > 0) {
|
||||
_senderCache.setText(
|
||||
|
@ -140,7 +141,11 @@ void MessageView::paint(
|
|||
? st::dialogsTextPaletteOver
|
||||
: st::dialogsTextPalette);
|
||||
p.setFont(st::dialogsTextFont);
|
||||
p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg));
|
||||
p.setPen(active
|
||||
? st::dialogsTextFgActive
|
||||
: selected
|
||||
? st::dialogsTextFgOver
|
||||
: st::dialogsTextFg);
|
||||
const auto guard = gsl::finally([&] {
|
||||
p.restoreTextPalette();
|
||||
});
|
||||
|
@ -161,7 +166,10 @@ void MessageView::paint(
|
|||
if (rect.width() < st::dialogsMiniPreview) {
|
||||
break;
|
||||
}
|
||||
p.drawImage(rect.x(), rect.y() + st::dialogsMiniPreviewTop, image);
|
||||
p.drawImage(
|
||||
rect.x(),
|
||||
rect.y() + st::dialogsMiniPreviewTop,
|
||||
image.data);
|
||||
rect.setLeft(rect.x()
|
||||
+ st::dialogsMiniPreview
|
||||
+ st::dialogsMiniPreviewSkip);
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Ui {
|
|||
|
||||
namespace HistoryView {
|
||||
struct ToPreviewOptions;
|
||||
struct ItemPreviewImage;
|
||||
struct ItemPreview;
|
||||
} // namespace HistoryView
|
||||
|
||||
|
@ -31,6 +32,7 @@ public:
|
|||
~MessageView();
|
||||
|
||||
using ToPreviewOptions = HistoryView::ToPreviewOptions;
|
||||
using ItemPreviewImage = HistoryView::ItemPreviewImage;
|
||||
using ItemPreview = HistoryView::ItemPreview;
|
||||
|
||||
void itemInvalidated(not_null<const HistoryItem*> item);
|
||||
|
@ -50,7 +52,7 @@ private:
|
|||
mutable const HistoryItem *_textCachedFor = nullptr;
|
||||
mutable Ui::Text::String _senderCache;
|
||||
mutable Ui::Text::String _textCache;
|
||||
mutable std::vector<QImage> _imagesCache;
|
||||
mutable std::vector<ItemPreviewImage> _imagesCache;
|
||||
mutable std::unique_ptr<LoadingContext> _loadingContext;
|
||||
|
||||
};
|
||||
|
|
|
@ -1253,27 +1253,31 @@ void History::addOlderSlice(const QVector<MTPMessage> &slice) {
|
|||
}
|
||||
|
||||
if (const auto added = createItems(slice); !added.empty()) {
|
||||
startBuildingFrontBlock(added.size());
|
||||
for (const auto &item : added) {
|
||||
addItemToBlock(item);
|
||||
}
|
||||
finishBuildingFrontBlock();
|
||||
|
||||
if (loadedAtBottom()) {
|
||||
// Add photos to overview and authors to lastAuthors.
|
||||
addItemsToLists(added);
|
||||
}
|
||||
addToSharedMedia(added);
|
||||
addCreatedOlderSlice(added);
|
||||
} else {
|
||||
// If no items were added it means we've loaded everything old.
|
||||
_loadedAtTop = true;
|
||||
addEdgesToSharedMedia();
|
||||
}
|
||||
|
||||
checkLocalMessages();
|
||||
checkLastMessage();
|
||||
}
|
||||
|
||||
void History::addCreatedOlderSlice(
|
||||
const std::vector<not_null<HistoryItem*>> &items) {
|
||||
startBuildingFrontBlock(items.size());
|
||||
for (const auto &item : items) {
|
||||
addItemToBlock(item);
|
||||
}
|
||||
finishBuildingFrontBlock();
|
||||
|
||||
if (loadedAtBottom()) {
|
||||
// Add photos to overview and authors to lastAuthors.
|
||||
addItemsToLists(items);
|
||||
}
|
||||
addToSharedMedia(items);
|
||||
}
|
||||
|
||||
void History::addNewerSlice(const QVector<MTPMessage> &slice) {
|
||||
bool wasLoadedAtBottom = loadedAtBottom();
|
||||
|
||||
|
@ -2266,6 +2270,14 @@ void History::setChatListMessage(HistoryItem *item) {
|
|||
}
|
||||
_chatListMessage = item;
|
||||
setChatListTimeId(item->date());
|
||||
|
||||
// If we have a single message from a group, request the full album.
|
||||
if (hasOrphanMediaGroupPart()
|
||||
&& !item->toPreview({
|
||||
.hideSender = true,
|
||||
.hideCaption = true }).images.empty()) {
|
||||
owner().histories().requestGroupAround(item);
|
||||
}
|
||||
} else if (!_chatListMessage || *_chatListMessage) {
|
||||
_chatListMessage = nullptr;
|
||||
updateChatListEntry();
|
||||
|
@ -2422,6 +2434,44 @@ void History::setFakeChatListMessageFrom(const MTPmessages_Messages &data) {
|
|||
setChatListMessage(item);
|
||||
}
|
||||
|
||||
void History::applyChatListGroup(
|
||||
ChannelId channelId,
|
||||
const MTPmessages_Messages &data) {
|
||||
if (!isEmpty()
|
||||
|| !_chatListMessage
|
||||
|| !*_chatListMessage
|
||||
|| (*_chatListMessage)->history()->channelId() != channelId
|
||||
|| (*_chatListMessage)->history() != this
|
||||
|| !_lastMessage
|
||||
|| !*_lastMessage) {
|
||||
return;
|
||||
}
|
||||
// Apply loaded album as a last slice.
|
||||
const auto processMessages = [&](const MTPVector<MTPMessage> &messages) {
|
||||
auto items = std::vector<not_null<HistoryItem*>>();
|
||||
items.reserve(messages.v.size());
|
||||
for (const auto &message : messages.v) {
|
||||
const auto id = IdFromMessage(message);
|
||||
if (const auto message = owner().message(channelId, id)) {
|
||||
items.push_back(message);
|
||||
}
|
||||
}
|
||||
if (!ranges::contains(items, not_null(*_lastMessage))
|
||||
|| !ranges::contains(items, not_null(*_chatListMessage))) {
|
||||
return;
|
||||
}
|
||||
_loadedAtBottom = true;
|
||||
ranges::sort(items, ranges::less{}, &HistoryItem::id);
|
||||
addCreatedOlderSlice(items);
|
||||
checkLocalMessages();
|
||||
checkLastMessage();
|
||||
};
|
||||
data.match([&](const MTPDmessages_messagesNotModified &) {
|
||||
}, [&](const auto &data) {
|
||||
processMessages(data.vmessages());
|
||||
});
|
||||
}
|
||||
|
||||
HistoryItem *History::lastMessage() const {
|
||||
return _lastMessage.value_or(nullptr);
|
||||
}
|
||||
|
|
|
@ -409,6 +409,10 @@ public:
|
|||
void setFakeChatListMessageFrom(const MTPmessages_Messages &data);
|
||||
void checkChatListMessageRemoved(not_null<HistoryItem*> item);
|
||||
|
||||
void applyChatListGroup(
|
||||
ChannelId channelId,
|
||||
const MTPmessages_Messages &data);
|
||||
|
||||
void forgetScrollState() {
|
||||
scrollTopItem = nullptr;
|
||||
}
|
||||
|
@ -516,6 +520,9 @@ private:
|
|||
return _buildingFrontBlock != nullptr;
|
||||
}
|
||||
|
||||
void addCreatedOlderSlice(
|
||||
const std::vector<not_null<HistoryItem*>> &items);
|
||||
|
||||
void checkForLoadedAtTop(not_null<HistoryItem*> added);
|
||||
void mainViewRemoved(
|
||||
not_null<HistoryBlock*> block,
|
||||
|
|
|
@ -57,20 +57,30 @@ enum class PointState : char;
|
|||
enum class Context : char;
|
||||
class ElementDelegate;
|
||||
|
||||
struct ToPreviewOptions {
|
||||
bool hideSender = false;
|
||||
bool hideCaption = false;
|
||||
bool generateImages = true;
|
||||
bool ignoreGroup = false;
|
||||
struct ItemPreviewImage {
|
||||
QImage data;
|
||||
uint64 cacheKey = 0;
|
||||
|
||||
explicit operator bool() const {
|
||||
return !data.isNull();
|
||||
}
|
||||
};
|
||||
|
||||
struct ItemPreview {
|
||||
QString text;
|
||||
std::vector<QImage> images;
|
||||
std::vector<ItemPreviewImage> images;
|
||||
int imagesInTextPosition = 0;
|
||||
std::any loadingContext;
|
||||
};
|
||||
|
||||
struct ToPreviewOptions {
|
||||
const std::vector<ItemPreviewImage> *existing = nullptr;
|
||||
bool hideSender = false;
|
||||
bool hideCaption = false;
|
||||
bool generateImages = true;
|
||||
bool ignoreGroup = false;
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
||||
|
||||
struct HiddenSenderInfo;
|
||||
|
|
|
@ -804,7 +804,7 @@ void Notification::updateNotifyDisplay() {
|
|||
p.setTextPalette(st::dialogsTextPalette);
|
||||
p.setPen(st::dialogsTextFg);
|
||||
p.setFont(st::dialogsTextFont);
|
||||
const auto text = _item // #TODO minis use images
|
||||
const auto text = _item
|
||||
? _item->toPreview({
|
||||
.hideSender = reminder,
|
||||
.generateImages = false,
|
||||
|
|
Loading…
Add table
Reference in a new issue