mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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([=](
|
)).done([=](
|
||||||
const MTPmessages_Messages &result,
|
const MTPmessages_Messages &result,
|
||||||
mtpRequestId requestId) {
|
mtpRequestId requestId) {
|
||||||
gotMessageDatas(nullptr, result, requestId);
|
_session->data().processExistingMessages(nullptr, result);
|
||||||
|
finalizeMessageDataRequest(nullptr, requestId);
|
||||||
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
|
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
|
||||||
finalizeMessageDataRequest(nullptr, requestId);
|
finalizeMessageDataRequest(nullptr, requestId);
|
||||||
}).afterDelay(kSmallDelayMs).send();
|
}).afterDelay(kSmallDelayMs).send();
|
||||||
|
@ -586,7 +587,8 @@ void ApiWrap::resolveMessageDatas() {
|
||||||
)).done([=](
|
)).done([=](
|
||||||
const MTPmessages_Messages &result,
|
const MTPmessages_Messages &result,
|
||||||
mtpRequestId requestId) {
|
mtpRequestId requestId) {
|
||||||
gotMessageDatas(channel, result, requestId);
|
_session->data().processExistingMessages(channel, result);
|
||||||
|
finalizeMessageDataRequest(channel, requestId);
|
||||||
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
|
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
|
||||||
finalizeMessageDataRequest(channel, requestId);
|
finalizeMessageDataRequest(channel, requestId);
|
||||||
}).afterDelay(kSmallDelayMs).send();
|
}).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(
|
void ApiWrap::finalizeMessageDataRequest(
|
||||||
ChannelData *channel,
|
ChannelData *channel,
|
||||||
mtpRequestId requestId) {
|
mtpRequestId requestId) {
|
||||||
|
|
|
@ -458,7 +458,6 @@ private:
|
||||||
void saveDraftsToCloud();
|
void saveDraftsToCloud();
|
||||||
|
|
||||||
void resolveMessageDatas();
|
void resolveMessageDatas();
|
||||||
void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId requestId);
|
|
||||||
void finalizeMessageDataRequest(
|
void finalizeMessageDataRequest(
|
||||||
ChannelData *channel,
|
ChannelData *channel,
|
||||||
mtpRequestId requestId);
|
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) {
|
void Histories::sendPendingReadInbox(not_null<History*> history) {
|
||||||
if (const auto state = lookup(history)) {
|
if (const auto state = lookup(history)) {
|
||||||
DEBUG_LOG(("Reading: send pending now with till %1 and when %2"
|
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 changeDialogUnreadMark(not_null<History*> history, bool unread);
|
||||||
void requestFakeChatListMessage(not_null<History*> history);
|
void requestFakeChatListMessage(not_null<History*> history);
|
||||||
|
|
||||||
|
void requestGroupAround(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
void deleteMessages(
|
void deleteMessages(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
const QVector<MTPint> &ids,
|
const QVector<MTPint> &ids,
|
||||||
|
@ -95,6 +97,10 @@ private:
|
||||||
bool sentReadDone = false;
|
bool sentReadDone = false;
|
||||||
bool postponedRequestEntry = false;
|
bool postponedRequestEntry = false;
|
||||||
};
|
};
|
||||||
|
struct ChatListGroupRequest {
|
||||||
|
MsgId aroundId = 0;
|
||||||
|
mtpRequestId requestId = 0;
|
||||||
|
};
|
||||||
|
|
||||||
void readInboxTill(not_null<History*> history, MsgId tillId, bool force);
|
void readInboxTill(not_null<History*> history, MsgId tillId, bool force);
|
||||||
void sendReadRequests();
|
void sendReadRequests();
|
||||||
|
@ -130,6 +136,10 @@ private:
|
||||||
|
|
||||||
base::flat_set<not_null<History*>> _fakeChatListRequests;
|
base::flat_set<not_null<History*>> _fakeChatListRequests;
|
||||||
|
|
||||||
|
base::flat_map<
|
||||||
|
not_null<History*>,
|
||||||
|
ChatListGroupRequest> _chatListGroupRequests;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -63,6 +63,7 @@ constexpr auto kFastRevokeRestriction = 24 * 60 * TimeId(60);
|
||||||
constexpr auto kMaxPreviewImages = 3;
|
constexpr auto kMaxPreviewImages = 3;
|
||||||
|
|
||||||
using ItemPreview = HistoryView::ItemPreview;
|
using ItemPreview = HistoryView::ItemPreview;
|
||||||
|
using ItemPreviewImage = HistoryView::ItemPreviewImage;
|
||||||
|
|
||||||
[[nodiscard]] Call ComputeCallData(const MTPDmessageActionPhoneCall &call) {
|
[[nodiscard]] Call ComputeCallData(const MTPDmessageActionPhoneCall &call) {
|
||||||
auto result = Call();
|
auto result = Call();
|
||||||
|
@ -191,25 +192,18 @@ using ItemPreview = HistoryView::ItemPreview;
|
||||||
return square;
|
return square;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PreparedPreview {
|
[[nodiscard]] ItemPreviewImage PreparePhotoPreview(
|
||||||
QImage preview;
|
|
||||||
bool loading = false;
|
|
||||||
|
|
||||||
explicit operator bool() const {
|
|
||||||
return !preview.isNull() || loading;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] PreparedPreview PreparePhotoPreview(
|
|
||||||
not_null<const HistoryItem*> item,
|
not_null<const HistoryItem*> item,
|
||||||
const std::shared_ptr<PhotoMedia> &media,
|
const std::shared_ptr<PhotoMedia> &media,
|
||||||
ImageRoundRadius radius) {
|
ImageRoundRadius radius) {
|
||||||
|
const auto photo = media->owner();
|
||||||
|
const auto readyCacheKey = reinterpret_cast<uint64>(photo.get());
|
||||||
if (const auto small = media->image(PhotoSize::Small)) {
|
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)) {
|
} 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)) {
|
} else if (const auto large = media->image(PhotoSize::Large)) {
|
||||||
return { PreparePreviewImage(large, radius) };
|
return { PreparePreviewImage(large, radius), readyCacheKey };
|
||||||
}
|
}
|
||||||
const auto allowedToDownload = [&] {
|
const auto allowedToDownload = [&] {
|
||||||
const auto photo = media->owner();
|
const auto photo = media->owner();
|
||||||
|
@ -223,29 +217,32 @@ struct PreparedPreview {
|
||||||
item->history()->peer,
|
item->history()->peer,
|
||||||
photo);
|
photo);
|
||||||
}();
|
}();
|
||||||
|
const auto cacheKey = allowedToDownload ? 0 : readyCacheKey;
|
||||||
if (allowedToDownload) {
|
if (allowedToDownload) {
|
||||||
media->owner()->load(PhotoSize::Small, item->fullId());
|
media->owner()->load(PhotoSize::Small, item->fullId());
|
||||||
}
|
}
|
||||||
if (const auto blurred = media->thumbnailInline()) {
|
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,
|
not_null<const HistoryItem*> item,
|
||||||
const std::shared_ptr<DocumentMedia> &media,
|
const std::shared_ptr<DocumentMedia> &media,
|
||||||
ImageRoundRadius radius) {
|
ImageRoundRadius radius) {
|
||||||
Expects(media->owner()->hasThumbnail());
|
Expects(media->owner()->hasThumbnail());
|
||||||
|
|
||||||
|
const auto document = media->owner();
|
||||||
|
const auto readyCacheKey = reinterpret_cast<uint64>(document.get());
|
||||||
if (const auto thumbnail = media->thumbnail()) {
|
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()) {
|
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) {
|
[[nodiscard]] QImage PutPlayIcon(QImage preview) {
|
||||||
|
@ -260,28 +257,17 @@ struct PreparedPreview {
|
||||||
return preview;
|
return preview;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] PreparedPreview PrepareFilePreview(
|
[[nodiscard]] ItemPreviewImage PrepareFilePreview(
|
||||||
not_null<const HistoryItem*> item,
|
not_null<const HistoryItem*> item,
|
||||||
const std::shared_ptr<DocumentMedia> &media,
|
const std::shared_ptr<DocumentMedia> &media,
|
||||||
ImageRoundRadius radius) {
|
ImageRoundRadius radius) {
|
||||||
if (auto result = PrepareFilePreviewImage(item, media, radius)) {
|
auto result = PrepareFilePreviewImage(item, media, radius);
|
||||||
const auto document = media->owner();
|
const auto document = media->owner();
|
||||||
if (!result.preview.isNull()
|
if (!result.data.isNull()
|
||||||
&& (document->isVideoFile() || document->isVideoMessage())) {
|
&& (document->isVideoFile() || document->isVideoMessage())) {
|
||||||
result.preview = PutPlayIcon(std::move(result.preview));
|
result.data = PutPlayIcon(std::move(result.data));
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
Expects(media->owner()->hasThumbnail());
|
return result;
|
||||||
|
|
||||||
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 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool TryFilePreview(not_null<DocumentData*> document) {
|
[[nodiscard]] bool TryFilePreview(not_null<DocumentData*> document) {
|
||||||
|
@ -290,6 +276,20 @@ struct PreparedPreview {
|
||||||
&& !document->isAudioFile();
|
&& !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
|
} // namespace
|
||||||
|
|
||||||
TextForMimeData WithCaptionClipboardText(
|
TextForMimeData WithCaptionClipboardText(
|
||||||
|
@ -554,22 +554,27 @@ ItemPreview MediaPhoto::toPreview(ToPreviewOptions options) const {
|
||||||
return toGroupPreview(group->items, options);
|
return toGroupPreview(group->items, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto caption = options.hideCaption
|
auto images = std::vector<ItemPreviewImage>();
|
||||||
? QString()
|
|
||||||
: parent()->originalText().text;
|
|
||||||
auto images = std::vector<QImage>();
|
|
||||||
const auto media = _photo->createMediaView();
|
|
||||||
const auto radius = _chat
|
|
||||||
? ImageRoundRadius::Ellipse
|
|
||||||
: ImageRoundRadius::Small;
|
|
||||||
auto context = std::any();
|
auto context = std::any();
|
||||||
if (auto prepared = PreparePhotoPreview(parent(), media, radius)) {
|
if (auto cached = FindCachedPreview(options.existing, _photo)) {
|
||||||
images.push_back(std::move(prepared.preview));
|
images.push_back(std::move(cached));
|
||||||
if (prepared.loading) {
|
} else {
|
||||||
context = media;
|
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 type = tr::lng_in_dlg_photo(tr::now);
|
||||||
|
const auto caption = options.hideCaption
|
||||||
|
? QString()
|
||||||
|
: parent()->originalText().text;
|
||||||
return {
|
return {
|
||||||
.text = WithCaptionDialogsText(type, caption, !images.empty()),
|
.text = WithCaptionDialogsText(type, caption, !images.empty()),
|
||||||
.images = std::move(images),
|
.images = std::move(images),
|
||||||
|
@ -751,6 +756,23 @@ ItemPreview MediaFile::toPreview(ToPreviewOptions options) const {
|
||||||
if (const auto sticker = _document->sticker()) {
|
if (const auto sticker = _document->sticker()) {
|
||||||
return Media::toPreview(options);
|
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 = [&] {
|
const auto type = [&] {
|
||||||
using namespace Ui::Text;
|
using namespace Ui::Text;
|
||||||
if (_document->isVideoMessage()) {
|
if (_document->isVideoMessage()) {
|
||||||
|
@ -772,22 +794,6 @@ ItemPreview MediaFile::toPreview(ToPreviewOptions options) const {
|
||||||
const auto caption = options.hideCaption
|
const auto caption = options.hideCaption
|
||||||
? QString()
|
? QString()
|
||||||
: parent()->originalText().text;
|
: 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 {
|
return {
|
||||||
.text = WithCaptionDialogsText(type, caption, !images.empty()),
|
.text = WithCaptionDialogsText(type, caption, !images.empty()),
|
||||||
.images = std::move(images),
|
.images = std::move(images),
|
||||||
|
|
|
@ -28,6 +28,7 @@ enum class Context : char;
|
||||||
class Element;
|
class Element;
|
||||||
class Media;
|
class Media;
|
||||||
struct ItemPreview;
|
struct ItemPreview;
|
||||||
|
struct ItemPreviewImage;
|
||||||
struct ToPreviewOptions;
|
struct ToPreviewOptions;
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
||||||
|
@ -75,6 +76,7 @@ public:
|
||||||
not_null<HistoryItem*> parent() const;
|
not_null<HistoryItem*> parent() const;
|
||||||
|
|
||||||
using ToPreviewOptions = HistoryView::ToPreviewOptions;
|
using ToPreviewOptions = HistoryView::ToPreviewOptions;
|
||||||
|
using ItemPreviewImage = HistoryView::ItemPreviewImage;
|
||||||
using ItemPreview = HistoryView::ItemPreview;
|
using ItemPreview = HistoryView::ItemPreview;
|
||||||
|
|
||||||
virtual std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) = 0;
|
virtual std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) = 0;
|
||||||
|
|
|
@ -1859,6 +1859,26 @@ void Session::processMessages(
|
||||||
processMessages(data.v, type);
|
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 {
|
const Session::Messages *Session::messagesList(ChannelId channelId) const {
|
||||||
if (channelId == NoChannel) {
|
if (channelId == NoChannel) {
|
||||||
return &_messages;
|
return &_messages;
|
||||||
|
|
|
@ -341,6 +341,9 @@ public:
|
||||||
void processMessages(
|
void processMessages(
|
||||||
const MTPVector<MTPMessage> &data,
|
const MTPVector<MTPMessage> &data,
|
||||||
NewMessageType type);
|
NewMessageType type);
|
||||||
|
void processExistingMessages(
|
||||||
|
ChannelData *channel,
|
||||||
|
const MTPmessages_Messages &data);
|
||||||
void processMessagesDeleted(
|
void processMessagesDeleted(
|
||||||
ChannelId channelId,
|
ChannelId channelId,
|
||||||
const QVector<MTPint> &data);
|
const QVector<MTPint> &data);
|
||||||
|
|
|
@ -105,6 +105,7 @@ void MessageView::paint(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_textCachedFor != item.get()) {
|
if (_textCachedFor != item.get()) {
|
||||||
|
options.existing = &_imagesCache;
|
||||||
auto preview = item->toPreview(options);
|
auto preview = item->toPreview(options);
|
||||||
if (!preview.images.empty() && preview.imagesInTextPosition > 0) {
|
if (!preview.images.empty() && preview.imagesInTextPosition > 0) {
|
||||||
_senderCache.setText(
|
_senderCache.setText(
|
||||||
|
@ -140,7 +141,11 @@ void MessageView::paint(
|
||||||
? st::dialogsTextPaletteOver
|
? st::dialogsTextPaletteOver
|
||||||
: st::dialogsTextPalette);
|
: st::dialogsTextPalette);
|
||||||
p.setFont(st::dialogsTextFont);
|
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([&] {
|
const auto guard = gsl::finally([&] {
|
||||||
p.restoreTextPalette();
|
p.restoreTextPalette();
|
||||||
});
|
});
|
||||||
|
@ -161,7 +166,10 @@ void MessageView::paint(
|
||||||
if (rect.width() < st::dialogsMiniPreview) {
|
if (rect.width() < st::dialogsMiniPreview) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
p.drawImage(rect.x(), rect.y() + st::dialogsMiniPreviewTop, image);
|
p.drawImage(
|
||||||
|
rect.x(),
|
||||||
|
rect.y() + st::dialogsMiniPreviewTop,
|
||||||
|
image.data);
|
||||||
rect.setLeft(rect.x()
|
rect.setLeft(rect.x()
|
||||||
+ st::dialogsMiniPreview
|
+ st::dialogsMiniPreview
|
||||||
+ st::dialogsMiniPreviewSkip);
|
+ st::dialogsMiniPreviewSkip);
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace Ui {
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
struct ToPreviewOptions;
|
struct ToPreviewOptions;
|
||||||
|
struct ItemPreviewImage;
|
||||||
struct ItemPreview;
|
struct ItemPreview;
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ public:
|
||||||
~MessageView();
|
~MessageView();
|
||||||
|
|
||||||
using ToPreviewOptions = HistoryView::ToPreviewOptions;
|
using ToPreviewOptions = HistoryView::ToPreviewOptions;
|
||||||
|
using ItemPreviewImage = HistoryView::ItemPreviewImage;
|
||||||
using ItemPreview = HistoryView::ItemPreview;
|
using ItemPreview = HistoryView::ItemPreview;
|
||||||
|
|
||||||
void itemInvalidated(not_null<const HistoryItem*> item);
|
void itemInvalidated(not_null<const HistoryItem*> item);
|
||||||
|
@ -50,7 +52,7 @@ private:
|
||||||
mutable const HistoryItem *_textCachedFor = nullptr;
|
mutable const HistoryItem *_textCachedFor = nullptr;
|
||||||
mutable Ui::Text::String _senderCache;
|
mutable Ui::Text::String _senderCache;
|
||||||
mutable Ui::Text::String _textCache;
|
mutable Ui::Text::String _textCache;
|
||||||
mutable std::vector<QImage> _imagesCache;
|
mutable std::vector<ItemPreviewImage> _imagesCache;
|
||||||
mutable std::unique_ptr<LoadingContext> _loadingContext;
|
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()) {
|
if (const auto added = createItems(slice); !added.empty()) {
|
||||||
startBuildingFrontBlock(added.size());
|
addCreatedOlderSlice(added);
|
||||||
for (const auto &item : added) {
|
|
||||||
addItemToBlock(item);
|
|
||||||
}
|
|
||||||
finishBuildingFrontBlock();
|
|
||||||
|
|
||||||
if (loadedAtBottom()) {
|
|
||||||
// Add photos to overview and authors to lastAuthors.
|
|
||||||
addItemsToLists(added);
|
|
||||||
}
|
|
||||||
addToSharedMedia(added);
|
|
||||||
} else {
|
} else {
|
||||||
// If no items were added it means we've loaded everything old.
|
// If no items were added it means we've loaded everything old.
|
||||||
_loadedAtTop = true;
|
_loadedAtTop = true;
|
||||||
addEdgesToSharedMedia();
|
addEdgesToSharedMedia();
|
||||||
}
|
}
|
||||||
|
|
||||||
checkLocalMessages();
|
checkLocalMessages();
|
||||||
checkLastMessage();
|
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) {
|
void History::addNewerSlice(const QVector<MTPMessage> &slice) {
|
||||||
bool wasLoadedAtBottom = loadedAtBottom();
|
bool wasLoadedAtBottom = loadedAtBottom();
|
||||||
|
|
||||||
|
@ -2266,6 +2270,14 @@ void History::setChatListMessage(HistoryItem *item) {
|
||||||
}
|
}
|
||||||
_chatListMessage = item;
|
_chatListMessage = item;
|
||||||
setChatListTimeId(item->date());
|
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) {
|
} else if (!_chatListMessage || *_chatListMessage) {
|
||||||
_chatListMessage = nullptr;
|
_chatListMessage = nullptr;
|
||||||
updateChatListEntry();
|
updateChatListEntry();
|
||||||
|
@ -2422,6 +2434,44 @@ void History::setFakeChatListMessageFrom(const MTPmessages_Messages &data) {
|
||||||
setChatListMessage(item);
|
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 {
|
HistoryItem *History::lastMessage() const {
|
||||||
return _lastMessage.value_or(nullptr);
|
return _lastMessage.value_or(nullptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -409,6 +409,10 @@ public:
|
||||||
void setFakeChatListMessageFrom(const MTPmessages_Messages &data);
|
void setFakeChatListMessageFrom(const MTPmessages_Messages &data);
|
||||||
void checkChatListMessageRemoved(not_null<HistoryItem*> item);
|
void checkChatListMessageRemoved(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
|
void applyChatListGroup(
|
||||||
|
ChannelId channelId,
|
||||||
|
const MTPmessages_Messages &data);
|
||||||
|
|
||||||
void forgetScrollState() {
|
void forgetScrollState() {
|
||||||
scrollTopItem = nullptr;
|
scrollTopItem = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -516,6 +520,9 @@ private:
|
||||||
return _buildingFrontBlock != nullptr;
|
return _buildingFrontBlock != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addCreatedOlderSlice(
|
||||||
|
const std::vector<not_null<HistoryItem*>> &items);
|
||||||
|
|
||||||
void checkForLoadedAtTop(not_null<HistoryItem*> added);
|
void checkForLoadedAtTop(not_null<HistoryItem*> added);
|
||||||
void mainViewRemoved(
|
void mainViewRemoved(
|
||||||
not_null<HistoryBlock*> block,
|
not_null<HistoryBlock*> block,
|
||||||
|
|
|
@ -57,20 +57,30 @@ enum class PointState : char;
|
||||||
enum class Context : char;
|
enum class Context : char;
|
||||||
class ElementDelegate;
|
class ElementDelegate;
|
||||||
|
|
||||||
struct ToPreviewOptions {
|
struct ItemPreviewImage {
|
||||||
bool hideSender = false;
|
QImage data;
|
||||||
bool hideCaption = false;
|
uint64 cacheKey = 0;
|
||||||
bool generateImages = true;
|
|
||||||
bool ignoreGroup = false;
|
explicit operator bool() const {
|
||||||
|
return !data.isNull();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ItemPreview {
|
struct ItemPreview {
|
||||||
QString text;
|
QString text;
|
||||||
std::vector<QImage> images;
|
std::vector<ItemPreviewImage> images;
|
||||||
int imagesInTextPosition = 0;
|
int imagesInTextPosition = 0;
|
||||||
std::any loadingContext;
|
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
|
} // namespace HistoryView
|
||||||
|
|
||||||
struct HiddenSenderInfo;
|
struct HiddenSenderInfo;
|
||||||
|
|
|
@ -804,7 +804,7 @@ void Notification::updateNotifyDisplay() {
|
||||||
p.setTextPalette(st::dialogsTextPalette);
|
p.setTextPalette(st::dialogsTextPalette);
|
||||||
p.setPen(st::dialogsTextFg);
|
p.setPen(st::dialogsTextFg);
|
||||||
p.setFont(st::dialogsTextFont);
|
p.setFont(st::dialogsTextFont);
|
||||||
const auto text = _item // #TODO minis use images
|
const auto text = _item
|
||||||
? _item->toPreview({
|
? _item->toPreview({
|
||||||
.hideSender = reminder,
|
.hideSender = reminder,
|
||||||
.generateImages = false,
|
.generateImages = false,
|
||||||
|
|
Loading…
Add table
Reference in a new issue