Use HistoryMedia as view, add Data::Media.

This commit is contained in:
John Preston 2018-01-14 19:02:25 +03:00
parent 97a9089ebf
commit 7425e80f05
66 changed files with 4463 additions and 3130 deletions

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_web_page.h" #include "data/data_web_page.h"
#include "data/data_feed.h" #include "data/data_feed.h"
#include "data/data_media_types.h"
#include "core/tl_help.h" #include "core/tl_help.h"
#include "base/overload.h" #include "base/overload.h"
#include "observer_peer.h" #include "observer_peer.h"
@ -2936,13 +2937,13 @@ void ApiWrap::sendUploadedPhoto(
const MTPInputFile &file, const MTPInputFile &file,
bool silent) { bool silent) {
if (const auto item = App::histItemById(localId)) { if (const auto item = App::histItemById(localId)) {
const auto caption = item->getMedia() const auto caption = item->media()
? item->getMedia()->getCaption() ? item->media()->caption()
: TextWithEntities(); : QString();
const auto media = MTP_inputMediaUploadedPhoto( const auto media = MTP_inputMediaUploadedPhoto(
MTP_flags(0), MTP_flags(0),
file, file,
MTP_string(caption.text), MTP_string(caption),
MTPVector<MTPInputDocument>(), MTPVector<MTPInputDocument>(),
MTP_int(0)); MTP_int(0));
if (const auto groupId = item->groupId()) { if (const auto groupId = item->groupId()) {
@ -2959,9 +2960,9 @@ void ApiWrap::sendUploadedDocument(
const base::optional<MTPInputFile> &thumb, const base::optional<MTPInputFile> &thumb,
bool silent) { bool silent) {
if (const auto item = App::histItemById(localId)) { if (const auto item = App::histItemById(localId)) {
auto media = item->getMedia(); auto media = item->media();
if (auto document = media ? media->getDocument() : nullptr) { if (auto document = media ? media->document() : nullptr) {
const auto caption = media->getCaption(); const auto caption = media->caption();
const auto groupId = item->groupId(); const auto groupId = item->groupId();
const auto flags = MTPDinputMediaUploadedDocument::Flags(0) const auto flags = MTPDinputMediaUploadedDocument::Flags(0)
| (thumb | (thumb
@ -2976,7 +2977,7 @@ void ApiWrap::sendUploadedDocument(
thumb ? *thumb : MTPInputFile(), thumb ? *thumb : MTPInputFile(),
MTP_string(document->mimeString()), MTP_string(document->mimeString()),
ComposeSendingDocumentAttributes(document), ComposeSendingDocumentAttributes(document),
MTP_string(caption.text), MTP_string(caption),
MTPVector<MTPInputDocument>(), MTPVector<MTPInputDocument>(),
MTP_int(0)); MTP_int(0));
if (groupId) { if (groupId) {
@ -3012,10 +3013,10 @@ void ApiWrap::uploadAlbumMedia(
if (!item) { if (!item) {
failed(); failed();
} }
if (const auto media = item->getMedia()) { if (const auto media = item->media()) {
if (const auto photo = media->getPhoto()) { if (const auto photo = media->photo()) {
photo->setWaitingForAlbum(); photo->setWaitingForAlbum();
} else if (const auto document = media->getDocument()) { } else if (const auto document = media->document()) {
document->setWaitingForAlbum(); document->setWaitingForAlbum();
} }
} }

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "data/data_abstract_structure.h" #include "data/data_abstract_structure.h"
#include "data/data_media_types.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_location_manager.h" #include "history/history_location_manager.h"
@ -978,10 +979,10 @@ namespace {
void checkSavedGif(HistoryItem *item) { void checkSavedGif(HistoryItem *item) {
if (!item->Has<HistoryMessageForwarded>() && (item->out() || item->history()->peer == App::self())) { if (!item->Has<HistoryMessageForwarded>() && (item->out() || item->history()->peer == App::self())) {
if (auto media = item->getMedia()) { if (const auto media = item->media()) {
if (auto doc = media->getDocument()) { if (const auto document = media->document()) {
if (doc->isGifv()) { if (document->isGifv()) {
addSavedGif(doc); addSavedGif(document);
} }
} }
} }
@ -1836,24 +1837,6 @@ namespace {
} }
} }
void messageViewDestroyed(not_null<HistoryView::Element*> view) {
if (::hoveredItem == view) {
hoveredItem(nullptr);
}
if (::pressedItem == view) {
pressedItem(nullptr);
}
if (::hoveredLinkItem == view) {
hoveredLinkItem(nullptr);
}
if (::pressedLinkItem == view) {
pressedLinkItem(nullptr);
}
if (::mousedItem == view) {
mousedItem(nullptr);
}
}
void historyUnregItem(HistoryItem *item) { void historyUnregItem(HistoryItem *item) {
auto data = fetchMsgsData(item->channelId(), false); auto data = fetchMsgsData(item->channelId(), false);
if (!data) return; if (!data) return;
@ -2474,11 +2457,12 @@ namespace {
if (!::gifItems.isEmpty()) { if (!::gifItems.isEmpty()) {
auto gifs = ::gifItems; auto gifs = ::gifItems;
for_const (auto item, gifs) { for_const (auto item, gifs) {
if (auto media = item->getMedia()) { // #TODO GIFs
if (!media->isRoundVideoPlaying()) { //if (auto media = item->getMedia()) {
media->stopInline(); // if (!media->isRoundVideoPlaying()) {
} // media->stopInline();
} // }
//}
} }
} }
} }
@ -2486,9 +2470,9 @@ namespace {
QString phoneFromSharedContact(int32 userId) { QString phoneFromSharedContact(int32 userId) {
auto i = ::sharedContactItems.constFind(userId); auto i = ::sharedContactItems.constFind(userId);
if (i != ::sharedContactItems.cend() && !i->empty()) { if (i != ::sharedContactItems.cend() && !i->empty()) {
if (auto media = (*i->cbegin())->getMedia()) { if (const auto media = (*i->cbegin())->media()) {
if (media->type() == MediaTypeContact) { if (const auto contact = media->sharedContact()) {
return static_cast<HistoryContact*>(media)->phone(); return contact->phoneNumber;
} }
} }
} }

View file

@ -237,7 +237,6 @@ namespace App {
void historyClearItems(); void historyClearItems();
void historyRegDependency(HistoryItem *dependent, HistoryItem *dependency); void historyRegDependency(HistoryItem *dependent, HistoryItem *dependency);
void historyUnregDependency(HistoryItem *dependent, HistoryItem *dependency); void historyUnregDependency(HistoryItem *dependent, HistoryItem *dependency);
void messageViewDestroyed(not_null<HistoryView::Element*> view);
void historyRegRandom(uint64 randomId, const FullMsgId &itemId); void historyRegRandom(uint64 randomId, const FullMsgId &itemId);
void historyUnregRandom(uint64 randomId); void historyUnregRandom(uint64 randomId);

View file

@ -259,4 +259,6 @@ private:
const std::unique_ptr<Window::Notifications::System> _notifications; const std::unique_ptr<Window::Notifications::System> _notifications;
const std::unique_ptr<Core::Changelogs> _changelogs; const std::unique_ptr<Core::Changelogs> _changelogs;
rpl::lifetime _lifetime;
}; };

View file

@ -31,7 +31,7 @@ const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask) {
return i.value(); return i.value();
} }
const RuntimeComposerMetadata *RuntimeComposer::ZeroRuntimeComposerMetadata = GetRuntimeComposerMetadata(0); const RuntimeComposerMetadata *RuntimeComposerBase::ZeroRuntimeComposerMetadata = GetRuntimeComposerMetadata(0);
RuntimeComponentWrapStruct RuntimeComponentWraps[64]; RuntimeComponentWrapStruct RuntimeComponentWraps[64];

View file

@ -7,8 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
template <typename Base>
class RuntimeComposer; class RuntimeComposer;
typedef void(*RuntimeComponentConstruct)(void *location, RuntimeComposer *composer);
class RuntimeComposerBase;
typedef void(*RuntimeComponentConstruct)(void *location, RuntimeComposerBase *composer);
typedef void(*RuntimeComponentDestruct)(void *location); typedef void(*RuntimeComponentDestruct)(void *location);
typedef void(*RuntimeComponentMove)(void *location, void *waslocation); typedef void(*RuntimeComponentMove)(void *location, void *waslocation);
@ -38,8 +41,10 @@ struct CeilDivideMinimumOne {
extern RuntimeComponentWrapStruct RuntimeComponentWraps[64]; extern RuntimeComponentWrapStruct RuntimeComponentWraps[64];
extern QAtomicInt RuntimeComponentIndexLast; extern QAtomicInt RuntimeComponentIndexLast;
template <typename Type> template <typename Type, typename Base>
struct RuntimeComponent { struct RuntimeComponent {
using RuntimeComponentBase = Base;
RuntimeComponent() { RuntimeComponent() {
// While there is no std::aligned_alloc(). // While there is no std::aligned_alloc().
static_assert(alignof(Type) <= alignof(std::max_align_t), "Components should align to std::max_align_t!"); static_assert(alignof(Type) <= alignof(std::max_align_t), "Components should align to std::max_align_t!");
@ -76,7 +81,7 @@ struct RuntimeComponent {
} }
protected: protected:
static void RuntimeComponentConstruct(void *location, RuntimeComposer *composer) { static void RuntimeComponentConstruct(void *location, RuntimeComposerBase *composer) {
new (location) Type(); new (location) Type();
} }
static void RuntimeComponentDestruct(void *location) { static void RuntimeComponentDestruct(void *location) {
@ -134,9 +139,9 @@ private:
const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask); const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask);
class RuntimeComposer { class RuntimeComposerBase {
public: public:
RuntimeComposer(uint64 mask = 0) : _data(zerodata()) { RuntimeComposerBase(uint64 mask = 0) : _data(zerodata()) {
if (mask) { if (mask) {
auto meta = GetRuntimeComposerMetadata(mask); auto meta = GetRuntimeComposerMetadata(mask);
@ -169,9 +174,9 @@ public:
} }
} }
} }
RuntimeComposer(const RuntimeComposer &other) = delete; RuntimeComposerBase(const RuntimeComposerBase &other) = delete;
RuntimeComposer &operator=(const RuntimeComposer &other) = delete; RuntimeComposerBase &operator=(const RuntimeComposerBase &other) = delete;
~RuntimeComposer() { ~RuntimeComposerBase() {
if (_data != zerodata()) { if (_data != zerodata()) {
auto meta = _meta(); auto meta = _meta();
for (int i = 0; i < meta->last; ++i) { for (int i = 0; i < meta->last; ++i) {
@ -184,24 +189,10 @@ public:
} }
} }
template <typename Type>
bool Has() const {
return (_meta()->offsets[Type::Index()] >= sizeof(_meta()));
}
template <typename Type>
Type *Get() {
return static_cast<Type*>(_dataptr(_meta()->offsets[Type::Index()]));
}
template <typename Type>
const Type *Get() const {
return static_cast<const Type*>(_dataptr(_meta()->offsets[Type::Index()]));
}
protected: protected:
void UpdateComponents(uint64 mask = 0) { void UpdateComponents(uint64 mask = 0) {
if (!_meta()->equals(mask)) { if (!_meta()->equals(mask)) {
RuntimeComposer tmp(mask); RuntimeComposerBase tmp(mask);
tmp.swap(*this); tmp.swap(*this);
if (_data != zerodata() && tmp._data != zerodata()) { if (_data != zerodata() && tmp._data != zerodata()) {
auto meta = _meta(), wasmeta = tmp._meta(); auto meta = _meta(), wasmeta = tmp._meta();
@ -223,6 +214,9 @@ protected:
} }
private: private:
template <typename Base>
friend class RuntimeComposer;
static const RuntimeComposerMetadata *ZeroRuntimeComposerMetadata; static const RuntimeComposerMetadata *ZeroRuntimeComposerMetadata;
static void *zerodata() { static void *zerodata() {
return &ZeroRuntimeComposerMetadata; return &ZeroRuntimeComposerMetadata;
@ -239,8 +233,41 @@ private:
} }
void *_data = nullptr; void *_data = nullptr;
void swap(RuntimeComposer &other) { void swap(RuntimeComposerBase &other) {
std::swap(_data, other._data); std::swap(_data, other._data);
} }
}; };
template <typename Base>
class RuntimeComposer : public RuntimeComposerBase {
public:
using RuntimeComposerBase::RuntimeComposerBase;
template <
typename Type,
typename = std::enable_if_t<std::is_same_v<
typename Type::RuntimeComponentBase,
Base>>>
bool Has() const {
return (_meta()->offsets[Type::Index()] >= sizeof(_meta()));
}
template <
typename Type,
typename = std::enable_if_t<std::is_same_v<
typename Type::RuntimeComponentBase,
Base>>>
Type *Get() {
return static_cast<Type*>(_dataptr(_meta()->offsets[Type::Index()]));
}
template <
typename Type,
typename = std::enable_if_t<std::is_same_v<
typename Type::RuntimeComponentBase,
Base>>>
const Type *Get() const {
return static_cast<const Type*>(_dataptr(_meta()->offsets[Type::Index()]));
}
};

View file

@ -12,7 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/media_clip_reader.h" #include "media/media_clip_reader.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_media_types.h" #include "data/data_media_types.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "window/window_controller.h" #include "window/window_controller.h"
#include "mainwidget.h" #include "mainwidget.h"
@ -22,59 +24,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
EditCaptionBox::EditCaptionBox( EditCaptionBox::EditCaptionBox(
QWidget*, QWidget*,
not_null<HistoryMedia*> media, not_null<Data::Media*> media,
FullMsgId msgId) FullMsgId msgId)
: _msgId(msgId) { : _msgId(msgId) {
Expects(media->canEditCaption()); Expects(media->allowsEditCaption());
QSize dimensions; QSize dimensions;
ImagePtr image; ImagePtr image;
QString caption;
DocumentData *doc = nullptr; DocumentData *doc = nullptr;
switch (media->type()) { if (const auto photo = media->photo()) {
case MediaTypeGif: {
_animated = true;
doc = static_cast<HistoryGif*>(media.get())->getDocument();
dimensions = doc->dimensions;
image = doc->thumb;
} break;
case MediaTypePhoto: {
_photo = true; _photo = true;
auto photo = static_cast<HistoryPhoto*>(media.get())->getPhoto();
dimensions = QSize(photo->full->width(), photo->full->height()); dimensions = QSize(photo->full->width(), photo->full->height());
image = photo->full; image = photo->full;
} break; } else if (const auto document = media->document()) {
dimensions = document->dimensions;
case MediaTypeVideo: { image = document->thumb;
if (document->isAnimation()) {
_animated = true; _animated = true;
doc = static_cast<HistoryVideo*>(media.get())->getDocument(); } else if (document->isVideoFile()) {
dimensions = doc->dimensions;
image = doc->thumb;
} break;
case MediaTypeGrouped: {
if (const auto photo = media->getPhoto()) {
dimensions = QSize(photo->full->width(), photo->full->height());
image = photo->full;
_photo = true;
} else if (const auto doc = media->getDocument()) {
dimensions = doc->dimensions;
image = doc->thumb;
_animated = true; _animated = true;
} } else {
} break;
case MediaTypeFile:
case MediaTypeMusicFile:
case MediaTypeVoiceFile: {
_doc = true; _doc = true;
doc = static_cast<HistoryDocument*>(media.get())->getDocument();
image = doc->thumb;
} break;
} }
caption = media->getCaption().text; doc = document;
}
auto caption = media->caption();
if (!_animated && (dimensions.isEmpty() || doc || image->isNull())) { if (!_animated && (dimensions.isEmpty() || doc || image->isNull())) {
if (image->isNull()) { if (image->isNull()) {

View file

@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h" #include "boxes/abstract_box.h"
class HistoryMedia; namespace Data {
class Media;
} // namespace Data
namespace Ui { namespace Ui {
class InputArea; class InputArea;
@ -17,7 +19,7 @@ class InputArea;
class EditCaptionBox : public BoxContent, public RPCSender { class EditCaptionBox : public BoxContent, public RPCSender {
public: public:
EditCaptionBox(QWidget*, not_null<HistoryMedia*> media, FullMsgId msgId); EditCaptionBox(QWidget*, not_null<Data::Media*> media, FullMsgId msgId);
protected: protected:
void prepare() override; void prepare() override;

View file

@ -15,10 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_media_types.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "auth_session.h" #include "auth_session.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_types.h"
namespace Calls { namespace Calls {
namespace { namespace {
@ -178,10 +178,11 @@ BoxController::Row::Type BoxController::Row::ComputeType(
not_null<const HistoryItem*> item) { not_null<const HistoryItem*> item) {
if (item->out()) { if (item->out()) {
return Type::Out; return Type::Out;
} else if (auto media = item->getMedia()) { } else if (auto media = item->media()) {
if (media->type() == MediaTypeCall) { if (const auto call = media->call()) {
auto reason = static_cast<HistoryCall*>(media)->reason(); const auto reason = call->finishReason;
if (reason == HistoryCall::FinishReason::Busy || reason == HistoryCall::FinishReason::Missed) { if (reason == Data::Call::FinishReason::Busy
|| reason == Data::Call::FinishReason::Missed) {
return Type::Missed; return Type::Missed;
} }
} }

View file

@ -285,15 +285,15 @@ void DocumentOpenClickHandler::doOpen(
if (App::main()) App::main()->mediaMarkRead(data); if (App::main()) App::main()->mediaMarkRead(data);
} else if (data->size < App::kImageSizeLimit) { } else if (data->size < App::kImageSizeLimit) {
if (!data->data().isEmpty() && playAnimation) { if (!data->data().isEmpty() && playAnimation) {
if (action == ActionOnLoadPlayInline && context && context->getMedia()) { if (action == ActionOnLoadPlayInline && context) {
context->getMedia()->playInline(); Auth().data().requestItemPlayInline(context);
} else { } else {
Messenger::Instance().showDocument(data, context); Messenger::Instance().showDocument(data, context);
} }
} else if (location.accessEnable()) { } else if (location.accessEnable()) {
if (data->isAnimation() || QImageReader(location.name()).canRead()) { if (data->isAnimation() || QImageReader(location.name()).canRead()) {
if (action == ActionOnLoadPlayInline && context && context->getMedia()) { if (action == ActionOnLoadPlayInline && context) {
context->getMedia()->playInline(); Auth().data().requestItemPlayInline(context);
} else { } else {
Messenger::Instance().showDocument(data, context); Messenger::Instance().showDocument(data, context);
} }
@ -548,7 +548,10 @@ void DocumentData::performActionOnLoad() {
auto showImage = !isVideoFile() && (size < App::kImageSizeLimit); auto showImage = !isVideoFile() && (size < App::kImageSizeLimit);
auto playVoice = isVoiceMessage() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen); auto playVoice = isVoiceMessage() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen);
auto playMusic = isAudioFile() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen); auto playMusic = isAudioFile() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen);
auto playAnimation = isAnimation() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen) && showImage && item && item->getMedia(); auto playAnimation = isAnimation()
&& (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen)
&& showImage
&& item;
if (auto applyTheme = isTheme()) { if (auto applyTheme = isTheme()) {
if (!loc.isEmpty() && loc.accessEnable()) { if (!loc.isEmpty() && loc.accessEnable()) {
Messenger::Instance().showDocument(this, item); Messenger::Instance().showDocument(this, item);
@ -588,8 +591,8 @@ void DocumentData::performActionOnLoad() {
} }
} else if (playAnimation) { } else if (playAnimation) {
if (loaded()) { if (loaded()) {
if (_actionOnLoad == ActionOnLoadPlayInline && item->getMedia()) { if (_actionOnLoad == ActionOnLoadPlayInline && item) {
item->getMedia()->playInline(); Auth().data().requestItemPlayInline(item);
} else { } else {
Messenger::Instance().showDocument(this, item); Messenger::Instance().showDocument(this, item);
} }
@ -607,8 +610,8 @@ void DocumentData::performActionOnLoad() {
if (App::main()) App::main()->mediaMarkRead(this); if (App::main()) App::main()->mediaMarkRead(this);
} else if (loc.accessEnable()) { } else if (loc.accessEnable()) {
if (showImage && QImageReader(loc.name()).canRead()) { if (showImage && QImageReader(loc.name()).canRead()) {
if (_actionOnLoad == ActionOnLoadPlayInline && item && item->getMedia()) { if (_actionOnLoad == ActionOnLoadPlayInline && item) {
item->getMedia()->playInline(); Auth().data().requestItemPlayInline(item);
} else { } else {
Messenger::Instance().showDocument(this, item); Messenger::Instance().showDocument(this, item);
} }
@ -764,7 +767,7 @@ void DocumentData::cancel() {
void DocumentData::notifyLayoutChanged() const { void DocumentData::notifyLayoutChanged() const {
auto &items = App::documentItems(); auto &items = App::documentItems();
for (auto item : items.value(const_cast<DocumentData*>(this))) { for (auto item : items.value(const_cast<DocumentData*>(this))) {
Auth().data().markItemLayoutChanged(item); Auth().data().markItemLayoutChange(item);
} }
if (auto items = InlineBots::Layout::documentItems()) { if (auto items = InlineBots::Layout::documentItems()) {

View file

@ -0,0 +1,49 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_groups.h"
#include "history/history_item.h"
namespace Data {
void Groups::registerMessage(not_null<HistoryItem*> item) {
const auto groupId = item->groupId();
if (!groupId) {
return;
}
const auto i = _data.emplace(groupId, Group()).first;
i->second.items.push_back(item);
}
void Groups::unregisterMessage(not_null<HistoryItem*> item) {
const auto groupId = item->groupId();
if (!groupId) {
return;
}
const auto i = _data.find(groupId);
if (i != _data.end()) {
auto &group = i->second;
group.items.erase(
ranges::remove(group.items, item),
group.items.end());
if (group.items.empty()) {
_data.erase(i);
}
}
}
const Group *Groups::find(not_null<HistoryItem*> item) const {
const auto groupId = item->groupId();
if (!groupId) {
return nullptr;
}
const auto i = _data.find(groupId);
return (i != _data.end()) ? &i->second : nullptr;
}
} // namespace Data

View file

@ -0,0 +1,32 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_types.h"
namespace Data {
struct Group {
HistoryItemsList items;
};
class Groups {
public:
void registerMessage(not_null<HistoryItem*> item);
void unregisterMessage(not_null<HistoryItem*> item);
const Group *find(not_null<HistoryItem*> item) const;
private:
std::map<MessageGroupId, Group> _data;
std::map<MessageGroupId, MessageGroupId> _alias;
};
} // namespace Data

View file

@ -0,0 +1,932 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_media_types.h"
#include "history/history_media_types.h"
#include "history/history_item.h"
#include "history/history_location_manager.h"
#include "storage/storage_shared_media.h"
#include "storage/localstorage.h"
#include "lang/lang_keys.h"
#include "layout.h"
namespace Data {
namespace {
Call ComputeCallData(const MTPDmessageActionPhoneCall &call) {
auto result = Call();
result.finishReason = [&] {
if (call.has_reason()) {
switch (call.vreason.type()) {
case mtpc_phoneCallDiscardReasonBusy:
return CallFinishReason::Busy;
case mtpc_phoneCallDiscardReasonDisconnect:
return CallFinishReason::Disconnected;
case mtpc_phoneCallDiscardReasonHangup:
return CallFinishReason::Hangup;
case mtpc_phoneCallDiscardReasonMissed:
return CallFinishReason::Missed;
}
Unexpected("Call reason type.");
}
return CallFinishReason::Hangup;
}();
result.duration = call.has_duration() ? call.vduration.v : 0;;
return result;
}
Invoice ComputeInvoiceData(const MTPDmessageMediaInvoice &data) {
auto result = Invoice();
result.isTest = data.is_test();
result.amount = data.vtotal_amount.v;
result.currency = qs(data.vcurrency);
result.description = qs(data.vdescription);
result.title = TextUtilities::SingleLine(qs(data.vtitle));
if (data.has_receipt_msg_id()) {
result.receiptMsgId = data.vreceipt_msg_id.v;
}
if (data.has_photo() && data.vphoto.type() == mtpc_webDocument) {
auto &doc = data.vphoto.c_webDocument();
auto imageSize = QSize();
for (auto &attribute : doc.vattributes.v) {
if (attribute.type() == mtpc_documentAttributeImageSize) {
auto &size = attribute.c_documentAttributeImageSize();
imageSize = QSize(size.vw.v, size.vh.v);
break;
}
}
if (!imageSize.isEmpty()) {
auto thumbsize = shrinkToKeepAspect(imageSize.width(), imageSize.height(), 100, 100);
auto thumb = ImagePtr(thumbsize.width(), thumbsize.height());
auto mediumsize = shrinkToKeepAspect(imageSize.width(), imageSize.height(), 320, 320);
auto medium = ImagePtr(mediumsize.width(), mediumsize.height());
// We don't use size from WebDocument, because it is not reliable.
// It can be > 0 and different from the real size that we get in upload.WebFile result.
auto filesize = 0; // doc.vsize.v;
auto full = ImagePtr(WebFileImageLocation(imageSize.width(), imageSize.height(), doc.vdc_id.v, doc.vurl.v, doc.vaccess_hash.v), filesize);
auto photoId = rand_value<PhotoId>();
result.photo = App::photoSet(photoId, 0, 0, unixtime(), thumb, medium, full);
}
}
return result;
}
QString WithCaptionDialogsText(
const QString &attachType,
const QString &caption) {
if (caption.isEmpty()) {
return textcmdLink(1, TextUtilities::Clean(attachType));
}
auto captionText = TextUtilities::Clean(caption);
auto attachTypeWrapped = textcmdLink(1, lng_dialogs_text_media_wrapped(
lt_media,
TextUtilities::Clean(attachType)));
return lng_dialogs_text_media(
lt_media_part,
attachTypeWrapped,
lt_caption,
captionText);
}
QString WithCaptionNotificationText(
const QString &attachType,
const QString &caption) {
if (caption.isEmpty()) {
return attachType;
}
auto attachTypeWrapped = lng_dialogs_text_media_wrapped(
lt_media,
attachType);
return lng_dialogs_text_media(
lt_media_part,
attachTypeWrapped,
lt_caption,
caption);
}
} // namespace
Media::Media(not_null<HistoryItem*> parent) : _parent(parent) {
}
not_null<HistoryItem*> Media::parent() const {
return _parent;
}
DocumentData *Media::document() const {
return nullptr;
}
PhotoData *Media::photo() const {
return nullptr;
}
WebPageData *Media::webpage() const {
return nullptr;
}
const SharedContact *Media::sharedContact() const {
return nullptr;
}
const Call *Media::call() const {
return nullptr;
}
GameData *Media::game() const {
return nullptr;
}
const Invoice *Media::invoice() const {
return nullptr;
}
LocationData *Media::location() const {
return nullptr;
}
bool Media::uploading() const {
return false;
}
Storage::SharedMediaTypesMask Media::sharedMediaTypes() const {
return {};
}
QString Media::caption() const {
return QString();
}
QString Media::chatsListText() const {
auto result = notificationText();
return result.isEmpty()
? QString()
: textcmdLink(1, TextUtilities::Clean(std::move(result)));
}
bool Media::hasReplyPreview() const {
return false;
}
ImagePtr Media::replyPreview() const {
return ImagePtr();
}
bool Media::allowsForward() const {
return true;
}
bool Media::allowsEdit() const {
return allowsEditCaption();
}
bool Media::allowsEditCaption() const {
return false;
}
bool Media::allowsRevoke() const {
return true;
}
bool Media::forwardedBecomesUnread() const {
return false;
}
QString Media::errorTextForForward(not_null<ChannelData*> channel) const {
return QString();
}
bool Media::consumeMessageText(const TextWithEntities &text) {
return false;
}
MediaPhoto::MediaPhoto(
not_null<HistoryItem*> parent,
not_null<PhotoData*> photo,
const QString &caption)
: Media(parent)
, _photo(photo)
, _caption(caption) {
App::regPhotoItem(_photo, parent);
}
MediaPhoto::MediaPhoto(
not_null<HistoryItem*> parent,
not_null<PeerData*> chat,
not_null<PhotoData*> photo)
: Media(parent)
, _photo(photo)
, _chat(chat) {
App::regPhotoItem(_photo, parent);
}
MediaPhoto::~MediaPhoto() {
App::unregPhotoItem(_photo, parent());
}
std::unique_ptr<Media> MediaPhoto::clone(not_null<HistoryItem*> parent) {
return _chat
? std::make_unique<MediaPhoto>(parent, _chat, _photo)
: std::make_unique<MediaPhoto>(parent, _photo, _caption);
}
PhotoData *MediaPhoto::photo() const {
return _photo;
}
bool MediaPhoto::uploading() const {
return _photo->uploading();
}
Storage::SharedMediaTypesMask MediaPhoto::sharedMediaTypes() const {
using Type = Storage::SharedMediaType;
if (_chat) {
return Type::ChatPhoto;
}
return Storage::SharedMediaTypesMask{}
.added(Type::Photo)
.added(Type::PhotoVideo);
}
QString MediaPhoto::caption() const {
return _caption;
}
QString MediaPhoto::notificationText() const {
return WithCaptionNotificationText(lang(lng_in_dlg_photo), _caption);
//return WithCaptionNotificationText(lang(lng_in_dlg_album), _caption);
}
QString MediaPhoto::chatsListText() const {
return WithCaptionDialogsText(lang(lng_in_dlg_photo), _caption);
//return WithCaptionDialogsText(lang(lng_in_dlg_album), _caption);
}
QString MediaPhoto::pinnedTextSubstring() const {
return lang(lng_action_pinned_media_photo);
}
bool MediaPhoto::allowsEditCaption() const {
return true;
}
QString MediaPhoto::errorTextForForward(
not_null<ChannelData*> channel) const {
if (channel->restricted(ChannelRestriction::f_send_media)) {
return lang(lng_restricted_send_media);
}
return QString();
}
bool MediaPhoto::updateInlineResultMedia(const MTPMessageMedia &media) {
if (media.type() != mtpc_messageMediaPhoto) {
return false;
}
auto &photo = media.c_messageMediaPhoto();
if (photo.has_photo() && !photo.has_ttl_seconds()) {
if (auto existing = App::feedPhoto(photo.vphoto)) {
if (existing == _photo) {
return true;
} else {
// collect data
}
}
} else {
LOG(("API Error: "
"Got MTPMessageMediaPhoto without photo "
"or with ttl_seconds in updateInlineResultMedia()"));
}
// Can return false if we collect the data.
return true;
}
bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) {
if (media.type() != mtpc_messageMediaPhoto) {
return false;
}
auto &mediaPhoto = media.c_messageMediaPhoto();
if (!mediaPhoto.has_photo() || mediaPhoto.has_ttl_seconds()) {
LOG(("Api Error: "
"Got MTPMessageMediaPhoto without photo "
"or with ttl_seconds in updateSentMedia()"));
return false;
}
const auto &photo = mediaPhoto.vphoto;
App::feedPhoto(photo, _photo);
if (photo.type() != mtpc_photo) {
return false;
}
auto &sizes = photo.c_photo().vsizes.v;
auto max = 0;
const MTPDfileLocation *maxLocation = 0;
for (const auto &data : sizes) {
char size = 0;
const MTPFileLocation *loc = 0;
switch (data.type()) {
case mtpc_photoSize: {
const auto &s = data.c_photoSize().vtype.v;
loc = &data.c_photoSize().vlocation;
if (s.size()) size = s[0];
} break;
case mtpc_photoCachedSize: {
const auto &s = data.c_photoCachedSize().vtype.v;
loc = &data.c_photoCachedSize().vlocation;
if (s.size()) size = s[0];
} break;
}
if (!loc || loc->type() != mtpc_fileLocation) {
continue;
}
if (size == 's') {
Local::writeImage(storageKey(loc->c_fileLocation()), _photo->thumb);
} else if (size == 'm') {
Local::writeImage(storageKey(loc->c_fileLocation()), _photo->medium);
} else if (size == 'x' && max < 1) {
max = 1;
maxLocation = &loc->c_fileLocation();
} else if (size == 'y' && max < 2) {
max = 2;
maxLocation = &loc->c_fileLocation();
//} else if (size == 'w' && max < 3) {
// max = 3;
// maxLocation = &loc->c_fileLocation();
}
}
if (maxLocation) {
Local::writeImage(storageKey(*maxLocation), _photo->full);
}
return true;
}
std::unique_ptr<HistoryMedia> MediaPhoto::createView(
not_null<HistoryView::Element*> message) {
if (_chat) {
return std::make_unique<HistoryPhoto>(
message,
_chat,
_photo,
st::msgServicePhotoWidth);
}
return std::make_unique<HistoryPhoto>(message, _photo, _caption);
}
MediaFile::MediaFile(
not_null<HistoryItem*> parent,
not_null<DocumentData*> document,
const QString &caption)
: Media(parent)
, _document(document)
, _caption(caption)
, _emoji(document->sticker() ? document->sticker()->alt : QString()) {
App::regDocumentItem(_document, parent);
if (!_emoji.isEmpty()) {
if (const auto emoji = Ui::Emoji::Find(_emoji)) {
_emoji = emoji->text();
}
}
}
MediaFile::~MediaFile() {
App::unregDocumentItem(_document, parent());
}
std::unique_ptr<Media> MediaFile::clone(not_null<HistoryItem*> parent) {
return std::make_unique<MediaFile>(parent, _document, _caption);
}
DocumentData *MediaFile::document() const {
return _document;
}
bool MediaFile::uploading() const {
return _document->uploading();
}
Storage::SharedMediaTypesMask MediaFile::sharedMediaTypes() const {
using Type = Storage::SharedMediaType;
if (_document->sticker()) {
return {};
} else if (_document->isVideoMessage()) {
return Storage::SharedMediaTypesMask{}
.added(Type::RoundFile)
.added(Type::RoundVoiceFile);
} else if (_document->isGifv()) {
return Type::GIF;
} else if (_document->isVideoFile()) {
return Storage::SharedMediaTypesMask{}
.added(Type::Video)
.added(Type::PhotoVideo);
} else if (_document->isVoiceMessage()) {
return Storage::SharedMediaTypesMask{}
.added(Type::VoiceFile)
.added(Type::RoundVoiceFile);
} else if (_document->isSharedMediaMusic()) {
return Type::MusicFile;
}
return Type::File;
}
QString MediaFile::chatsListText() const {
if (const auto sticker = _document->sticker()) {
return Media::chatsListText();
}
const auto type = [&] {
if (_document->isVideoMessage()) {
return lang(lng_in_dlg_video_message);
} else if (_document->isAnimation()) {
return qsl("GIF");
} else if (_document->isVideoFile()) {
return lang(lng_in_dlg_video);
} else if (_document->isVoiceMessage()) {
return lang(lng_in_dlg_audio);
} else if (!_document->filename().isEmpty()) {
return _document->filename();
} else if (_document->isAudioFile()) {
return lang(lng_in_dlg_audio_file);
}
return lang(lng_in_dlg_file);
}();
return WithCaptionDialogsText(type, _caption);
}
QString MediaFile::notificationText() const {
if (const auto sticker = _document->sticker()) {
return _emoji.isEmpty()
? lang(lng_in_dlg_sticker)
: lng_in_dlg_sticker_emoji(lt_emoji, _emoji);
}
const auto type = [&] {
if (_document->isVideoMessage()) {
return lang(lng_in_dlg_video_message);
} else if (_document->isAnimation()) {
return qsl("GIF");
} else if (_document->isVideoFile()) {
return lang(lng_in_dlg_video);
} else if (_document->isVoiceMessage()) {
return lang(lng_in_dlg_audio);
} else if (!_document->filename().isEmpty()) {
return _document->filename();
} else if (_document->isAudioFile()) {
return lang(lng_in_dlg_audio_file);
}
return lang(lng_in_dlg_file);
}();
return WithCaptionNotificationText(type, _caption);
}
QString MediaFile::pinnedTextSubstring() const {
if (const auto sticker = _document->sticker()) {
if (!_emoji.isEmpty()) {
return lng_action_pinned_media_emoji_sticker(lt_emoji, _emoji);
}
return lang(lng_action_pinned_media_sticker);
} else if (_document->isAnimation()) {
if (_document->isVideoMessage()) {
return lang(lng_action_pinned_media_video_message);
}
return lang(lng_action_pinned_media_gif);
} else if (_document->isVideoFile()) {
return lang(lng_action_pinned_media_video);
} else if (_document->isVoiceMessage()) {
return lang(lng_action_pinned_media_voice);
} else if (_document->isSong()) {
return lang(lng_action_pinned_media_audio);
}
return lang(lng_action_pinned_media_file);
}
bool MediaFile::allowsEditCaption() const {
return !_document->isVideoMessage() && !_document->sticker();
}
bool MediaFile::forwardedBecomesUnread() const {
return _document->isVoiceMessage()
//|| _document->isVideoFile()
|| _document->isVideoMessage();
}
QString MediaFile::errorTextForForward(
not_null<ChannelData*> channel) const {
if (const auto sticker = _document->sticker()) {
if (channel->restricted(ChannelRestriction::f_send_stickers)) {
return lang(lng_restricted_send_stickers);
}
} else if (_document->isAnimation()) {
if (_document->isVideoMessage()) {
if (channel->restricted(ChannelRestriction::f_send_media)) {
return lang(lng_restricted_send_media);
}
} else {
if (channel->restricted(ChannelRestriction::f_send_gifs)) {
return lang(lng_restricted_send_gifs);
}
}
} else if (channel->restricted(ChannelRestriction::f_send_media)) {
return lang(lng_restricted_send_media);
}
return QString();
}
bool MediaFile::updateInlineResultMedia(const MTPMessageMedia &media) {
if (media.type() != mtpc_messageMediaDocument) {
return false;
}
auto &data = media.c_messageMediaDocument();
if (data.has_document() && !data.has_ttl_seconds()) {
if (const auto document = App::feedDocument(data.vdocument)) {
if (document == _document) {
return false;
} else {
document->collectLocalData(_document);
}
}
} else {
LOG(("API Error: "
"Got MTPMessageMediaDocument without document "
"or with ttl_seconds in updateInlineResultMedia()"));
}
return false;
}
bool MediaFile::updateSentMedia(const MTPMessageMedia &media) {
if (media.type() != mtpc_messageMediaDocument) {
return false;
}
auto &data = media.c_messageMediaDocument();
if (!data.has_document() || data.has_ttl_seconds()) {
LOG(("Api Error: "
"Got MTPMessageMediaDocument without document "
"or with ttl_seconds in updateSentMedia()"));
return false;
}
const auto changed = App::feedDocument(data.vdocument, _document);
if (!changed->data().isEmpty()) {
if (changed->isVoiceMessage()) {
Local::writeAudio(changed->mediaKey(), changed->data());
} else {
Local::writeStickerImage(changed->mediaKey(), changed->data());
}
}
return true;
}
std::unique_ptr<HistoryMedia> MediaFile::createView(
not_null<HistoryView::Element*> message) {
if (_document->sticker()) {
return std::make_unique<HistorySticker>(message, _document);
} else if (_document->isAnimation()) {
return std::make_unique<HistoryGif>(message, _document, _caption);
} else if (_document->isVideoFile()) {
return std::make_unique<HistoryVideo>(message, _document, _caption);
}
return std::make_unique<HistoryDocument>(message, _document, _caption);
}
MediaContact::MediaContact(
not_null<HistoryItem*> parent,
UserId userId,
const QString &firstName,
const QString &lastName,
const QString &phoneNumber)
: Media(parent) {
_contact.userId = userId;
_contact.firstName = firstName;
_contact.lastName = lastName;
_contact.phoneNumber = phoneNumber;
}
std::unique_ptr<Media> MediaContact::clone(not_null<HistoryItem*> parent) {
return std::make_unique<MediaContact>(
parent,
_contact.userId,
_contact.firstName,
_contact.lastName,
_contact.phoneNumber);
}
const SharedContact *MediaContact::sharedContact() const {
return &_contact;
}
QString MediaContact::notificationText() const {
return lang(lng_in_dlg_contact);
}
QString MediaContact::pinnedTextSubstring() const {
return lang(lng_action_pinned_media_contact);
}
bool MediaContact::updateInlineResultMedia(const MTPMessageMedia &media) {
return false;
}
bool MediaContact::updateSentMedia(const MTPMessageMedia &media) {
if (media.type() != mtpc_messageMediaContact) {
return false;
}
if (_contact.userId != media.c_messageMediaContact().vuser_id.v) {
//detachFromParent(); // #TODO contacts
_contact.userId = media.c_messageMediaContact().vuser_id.v;
//attachToParent();
}
return true;
}
std::unique_ptr<HistoryMedia> MediaContact::createView(
not_null<HistoryView::Element*> message) {
return std::make_unique<HistoryContact>(
message,
_contact.userId,
_contact.firstName,
_contact.lastName,
_contact.phoneNumber);
}
MediaLocation::MediaLocation(
not_null<HistoryItem*> parent,
const LocationCoords &coords)
: MediaLocation(parent, coords, QString(), QString()) {
}
MediaLocation::MediaLocation(
not_null<HistoryItem*> parent,
const LocationCoords &coords,
const QString &title,
const QString &description)
: Media(parent)
, _location(App::location(coords))
, _title(title)
, _description(description) {
}
std::unique_ptr<Media> MediaLocation::clone(not_null<HistoryItem*> parent) {
return std::make_unique<MediaLocation>(
parent,
_location->coords,
_title,
_description);
}
LocationData *MediaLocation::location() const {
return _location;
}
QString MediaLocation::chatsListText() const {
return WithCaptionDialogsText(lang(lng_maps_point), _title);
}
QString MediaLocation::notificationText() const {
return WithCaptionNotificationText(lang(lng_maps_point), _title);
}
QString MediaLocation::pinnedTextSubstring() const {
return lang(lng_action_pinned_media_location);
}
bool MediaLocation::updateInlineResultMedia(const MTPMessageMedia &media) {
return false;
}
bool MediaLocation::updateSentMedia(const MTPMessageMedia &media) {
return false;
}
std::unique_ptr<HistoryMedia> MediaLocation::createView(
not_null<HistoryView::Element*> message) {
return std::make_unique<HistoryLocation>(
message,
_location,
_title,
_description);
}
MediaCall::MediaCall(
not_null<HistoryItem*> parent,
const MTPDmessageActionPhoneCall &call)
: Media(parent)
, _call(ComputeCallData(call)) {
}
std::unique_ptr<Media> MediaCall::clone(not_null<HistoryItem*> parent) {
Unexpected("Clone of call media.");
}
const Call *MediaCall::call() const {
return &_call;
}
QString MediaCall::notificationText() const {
auto result = Text(parent(), _call.finishReason);
if (_call.duration > 0) {
result = lng_call_type_and_duration(
lt_type,
result,
lt_duration,
formatDurationWords(_call.duration));
}
return result;
}
QString MediaCall::pinnedTextSubstring() const {
return QString();
}
bool MediaCall::allowsForward() const {
return false;
}
bool MediaCall::allowsRevoke() const {
return false;
}
bool MediaCall::updateInlineResultMedia(const MTPMessageMedia &media) {
return false;
}
bool MediaCall::updateSentMedia(const MTPMessageMedia &media) {
return false;
}
std::unique_ptr<HistoryMedia> MediaCall::createView(
not_null<HistoryView::Element*> message) {
return std::make_unique<HistoryCall>(message, &_call);
}
QString MediaCall::Text(
not_null<HistoryItem*> item,
CallFinishReason reason) {
if (item->out()) {
return lang(reason == CallFinishReason::Missed
? lng_call_cancelled
: lng_call_outgoing);
} else if (reason == CallFinishReason::Missed) {
return lang(lng_call_missed);
} else if (reason == CallFinishReason::Busy) {
return lang(lng_call_declined);
}
return lang(lng_call_incoming);
}
MediaWebPage::MediaWebPage(
not_null<HistoryItem*> parent,
not_null<WebPageData*> page)
: Media(parent)
, _page(page) {
}
std::unique_ptr<Media> MediaWebPage::clone(not_null<HistoryItem*> parent) {
return std::make_unique<MediaWebPage>(parent, _page);
}
WebPageData *MediaWebPage::webpage() const {
return _page;
}
QString MediaWebPage::notificationText() const {
return QString();
}
QString MediaWebPage::pinnedTextSubstring() const {
return QString();
}
bool MediaWebPage::allowsEdit() const {
return false;
}
bool MediaWebPage::updateInlineResultMedia(const MTPMessageMedia &media) {
return false;
}
bool MediaWebPage::updateSentMedia(const MTPMessageMedia &media) {
return false;
}
std::unique_ptr<HistoryMedia> MediaWebPage::createView(
not_null<HistoryView::Element*> message) {
return std::make_unique<HistoryWebPage>(message, _page);
}
MediaGame::MediaGame(
not_null<HistoryItem*> parent,
not_null<GameData*> game)
: Media(parent)
, _game(game) {
}
std::unique_ptr<Media> MediaGame::clone(not_null<HistoryItem*> parent) {
return std::make_unique<MediaGame>(parent, _game);
}
QString MediaGame::notificationText() const {
// Add a game controller emoji before game title.
auto result = QString();
result.reserve(_game->title.size() + 3);
result.append(
QChar(0xD83C)
).append(
QChar(0xDFAE)
).append(
QChar(' ')
).append(_game->title);
return result;
}
GameData *MediaGame::game() const {
return _game;
}
QString MediaGame::pinnedTextSubstring() const {
auto title = _game->title;
return lng_action_pinned_media_game(lt_game, title);
}
QString MediaGame::errorTextForForward(
not_null<ChannelData*> channel) const {
if (channel->restricted(ChannelRestriction::f_send_games)) {
return lang(lng_restricted_send_inline);
}
return QString();
}
bool MediaGame::consumeMessageText(const TextWithEntities &text) {
_consumedText = text;
return true;
}
bool MediaGame::updateInlineResultMedia(const MTPMessageMedia &media) {
return updateSentMedia(media);
}
bool MediaGame::updateSentMedia(const MTPMessageMedia &media) {
if (media.type() != mtpc_messageMediaGame) {
return false;
}
const auto &game = media.c_messageMediaGame().vgame;
if (game.type() == mtpc_game) {
App::feedGame(game.c_game(), _game);
}
return true;
}
std::unique_ptr<HistoryMedia> MediaGame::createView(
not_null<HistoryView::Element*> message) {
return std::make_unique<HistoryGame>(message, _game, _consumedText);
}
MediaInvoice::MediaInvoice(
not_null<HistoryItem*> parent,
const MTPDmessageMediaInvoice &data)
: Media(parent)
, _invoice(ComputeInvoiceData(data)) {
}
MediaInvoice::MediaInvoice(
not_null<HistoryItem*> parent,
const Invoice &data)
: Media(parent)
, _invoice(data) {
}
std::unique_ptr<Media> MediaInvoice::clone(not_null<HistoryItem*> parent) {
return std::make_unique<MediaInvoice>(parent, _invoice);
}
const Invoice *MediaInvoice::invoice() const {
return &_invoice;
}
QString MediaInvoice::notificationText() const {
return _invoice.title;
}
QString MediaInvoice::pinnedTextSubstring() const {
return QString();
}
bool MediaInvoice::updateInlineResultMedia(const MTPMessageMedia &media) {
return true;
}
bool MediaInvoice::updateSentMedia(const MTPMessageMedia &media) {
return true;
}
std::unique_ptr<HistoryMedia> MediaInvoice::createView(
not_null<HistoryView::Element*> message) {
return std::make_unique<HistoryInvoice>(message, &_invoice);
}
} // namespace Data

View file

@ -0,0 +1,347 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class HistoryItem;
class HistoryMedia;
namespace base {
template <typename Enum>
class enum_mask;
} // namespace base
namespace Storage {
enum class SharedMediaType : char;
using SharedMediaTypesMask = base::enum_mask<SharedMediaType>;
} // namespace Storage
namespace HistoryView {
enum class Context : char;
class Element;
} // namespace HistoryView
namespace Data {
enum class CallFinishReason : char {
Missed,
Busy,
Disconnected,
Hangup,
};
struct SharedContact {
UserId userId = 0;
QString firstName;
QString lastName;
QString phoneNumber;
};
struct Call {
using FinishReason = CallFinishReason;
int duration = 0;
FinishReason finishReason = FinishReason::Missed;
};
struct Invoice {
MsgId receiptMsgId = 0;
uint64 amount = 0;
QString currency;
QString title;
QString description;
PhotoData *photo = nullptr;
bool isTest = false;
};
class Media {
public:
Media(not_null<HistoryItem*> parent);
virtual ~Media() = default;
not_null<HistoryItem*> parent() const;
virtual std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) = 0;
virtual DocumentData *document() const;
virtual PhotoData *photo() const;
virtual WebPageData *webpage() const;
virtual const SharedContact *sharedContact() const;
virtual const Call *call() const;
virtual GameData *game() const;
virtual const Invoice *invoice() const;
virtual LocationData *location() const;
virtual bool uploading() const;
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const;
virtual QString caption() const;
virtual bool hasReplyPreview() const;
virtual ImagePtr replyPreview() const;
// Returns text with link-start and link-end commands for service-color highlighting.
// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
virtual QString chatsListText() const;
virtual QString notificationText() const = 0;
virtual QString pinnedTextSubstring() const = 0;
virtual bool allowsForward() const;
virtual bool allowsEdit() const;
virtual bool allowsEditCaption() const;
virtual bool allowsRevoke() const;
virtual bool forwardedBecomesUnread() const;
virtual QString errorTextForForward(
not_null<ChannelData*> channel) const;
[[nodiscard]] virtual bool consumeMessageText(
const TextWithEntities &text);
// After sending an inline result we may want to completely recreate
// the media (all media that was generated on client side, for example).
virtual bool updateInlineResultMedia(const MTPMessageMedia &media) = 0;
virtual bool updateSentMedia(const MTPMessageMedia &media) = 0;
virtual std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message) = 0;
private:
const not_null<HistoryItem*> _parent;
};
class MediaPhoto : public Media {
public:
MediaPhoto(
not_null<HistoryItem*> parent,
not_null<PhotoData*> photo,
const QString &caption);
MediaPhoto(
not_null<HistoryItem*> parent,
not_null<PeerData*> chat,
not_null<PhotoData*> photo);
~MediaPhoto();
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
PhotoData *photo() const override;
bool uploading() const override;
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
QString caption() const override;
QString chatsListText() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
bool allowsEditCaption() const override;
QString errorTextForForward(
not_null<ChannelData*> channel) const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message) override;
private:
not_null<PhotoData*> _photo;
PeerData *_chat = nullptr;
QString _caption;
};
class MediaFile : public Media {
public:
MediaFile(
not_null<HistoryItem*> parent,
not_null<DocumentData*> document,
const QString &caption);
~MediaFile();
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
DocumentData *document() const override;
bool uploading() const override;
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
QString chatsListText() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
bool allowsEditCaption() const override;
bool forwardedBecomesUnread() const override;
QString errorTextForForward(
not_null<ChannelData*> channel) const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message) override;
private:
not_null<DocumentData*> _document;
QString _caption;
QString _emoji;
};
class MediaContact : public Media {
public:
MediaContact(
not_null<HistoryItem*> parent,
UserId userId,
const QString &firstName,
const QString &lastName,
const QString &phoneNumber);
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
const SharedContact *sharedContact() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message) override;
private:
SharedContact _contact;
};
class MediaLocation : public Media {
public:
MediaLocation(
not_null<HistoryItem*> parent,
const LocationCoords &coords);
MediaLocation(
not_null<HistoryItem*> parent,
const LocationCoords &coords,
const QString &title,
const QString &description);
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
LocationData *location() const override;
QString chatsListText() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message) override;
private:
not_null<LocationData*> _location;
QString _title;
QString _description;
};
class MediaCall : public Media {
public:
MediaCall(
not_null<HistoryItem*> parent,
const MTPDmessageActionPhoneCall &call);
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
const Call *call() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
bool allowsForward() const override;
bool allowsRevoke() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message) override;
static QString Text(
not_null<HistoryItem*> item,
CallFinishReason reason);
private:
Call _call;
};
class MediaWebPage : public Media {
public:
MediaWebPage(
not_null<HistoryItem*> parent,
not_null<WebPageData*> page);
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
WebPageData *webpage() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
bool allowsEdit() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message) override;
private:
not_null<WebPageData*> _page;
};
class MediaGame : public Media {
public:
MediaGame(
not_null<HistoryItem*> parent,
not_null<GameData*> game);
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
GameData *game() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
QString errorTextForForward(
not_null<ChannelData*> channel) const override;
bool consumeMessageText(const TextWithEntities &text) override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message) override;
private:
not_null<GameData*> _game;
TextWithEntities _consumedText;
};
class MediaInvoice : public Media {
public:
MediaInvoice(
not_null<HistoryItem*> parent,
const MTPDmessageMediaInvoice &data);
MediaInvoice(
not_null<HistoryItem*> parent,
const Invoice &data);
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
const Invoice *invoice() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message) override;
private:
Invoice _invoice;
};
} // namespace Data

View file

@ -66,7 +66,7 @@ void PhotoData::notifyLayoutChanged() const {
auto i = items.constFind(const_cast<PhotoData*>(this)); auto i = items.constFind(const_cast<PhotoData*>(this));
if (i != items.cend()) { if (i != items.cend()) {
for_const (auto item, i.value()) { for_const (auto item, i.value()) {
Auth().data().markItemLayoutChanged(item); Auth().data().markItemLayoutChange(item);
} }
} }
} }

View file

@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "observer_peer.h" #include "observer_peer.h"
#include "history/history_item_components.h" #include "history/history_item_components.h"
#include "history/view/history_view_element.h"
#include "data/data_feed.h" #include "data/data_feed.h"
namespace Data { namespace Data {
using ViewElement = HistoryView::Element;
Session::Session() { Session::Session() {
Notify::PeerUpdateViewer( Notify::PeerUpdateViewer(
Notify::PeerUpdate::Flag::UserIsContact Notify::PeerUpdate::Flag::UserIsContact
@ -27,12 +30,61 @@ Session::Session() {
Session::~Session() = default; Session::~Session() = default;
void Session::markItemLayoutChanged(not_null<const HistoryItem*> item) { void Session::registerItemView(not_null<ViewElement*> view) {
_itemLayoutChanged.fire_copy(item); _views[view->data()].push_back(view);
}
void Session::unregisterItemView(not_null<ViewElement*> view) {
const auto i = _views.find(view->data());
if (i != _views.end()) {
auto &list = i->second;
list.erase(ranges::remove(list, view), end(list));
if (list.empty()) {
_views.erase(i);
}
}
if (App::hoveredItem() == view) {
App::hoveredItem(nullptr);
}
if (App::pressedItem() == view) {
App::pressedItem(nullptr);
}
if (App::hoveredLinkItem() == view) {
App::hoveredLinkItem(nullptr);
}
if (App::pressedLinkItem() == view) {
App::pressedLinkItem(nullptr);
}
if (App::mousedItem() == view) {
App::mousedItem(nullptr);
}
}
void Session::markItemLayoutChange(not_null<const HistoryItem*> item) {
_itemLayoutChanges.fire_copy(item);
} }
rpl::producer<not_null<const HistoryItem*>> Session::itemLayoutChanged() const { rpl::producer<not_null<const HistoryItem*>> Session::itemLayoutChanged() const {
return _itemLayoutChanged.events(); return _itemLayoutChanges.events();
}
void Session::markViewLayoutChange(not_null<const HistoryView::Element*> view) {
_viewLayoutChanges.fire_copy(view);
}
rpl::producer<not_null<const HistoryView::Element*>> Session::viewLayoutChanged() const {
return _viewLayoutChanges.events();
}
void Session::markItemIdChange(IdChange event) {
_itemIdChanges.fire_copy(event);
enumerateItemViews(event.item, [](not_null<HistoryView::Element*> view) {
view->refreshDataId();
});
}
rpl::producer<Session::IdChange> Session::itemIdChanged() const {
return _itemIdChanges.events();
} }
void Session::requestItemRepaint(not_null<const HistoryItem*> item) { void Session::requestItemRepaint(not_null<const HistoryItem*> item) {
@ -43,6 +95,14 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const
return _itemRepaintRequest.events(); return _itemRepaintRequest.events();
} }
void Session::requestViewRepaint(not_null<const ViewElement*> view) {
_viewRepaintRequest.fire_copy(view);
}
rpl::producer<not_null<const ViewElement*>> Session::viewRepaintRequest() const {
return _viewRepaintRequest.events();
}
void Session::requestItemViewResize(not_null<const HistoryItem*> item) { void Session::requestItemViewResize(not_null<const HistoryItem*> item) {
_itemViewResizeRequest.fire_copy(item); _itemViewResizeRequest.fire_copy(item);
} }
@ -51,6 +111,14 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemViewResizeRequest() con
return _itemViewResizeRequest.events(); return _itemViewResizeRequest.events();
} }
void Session::requestViewResize(not_null<const ViewElement*> view) {
_viewResizeRequest.fire_copy(view);
}
rpl::producer<not_null<const ViewElement*>> Session::viewResizeRequest() const {
return _viewResizeRequest.events();
}
void Session::requestItemViewRefresh(not_null<const HistoryItem*> item) { void Session::requestItemViewRefresh(not_null<const HistoryItem*> item) {
_itemViewRefreshRequest.fire_copy(item); _itemViewRefreshRequest.fire_copy(item);
} }
@ -59,6 +127,14 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemViewRefreshRequest() co
return _itemViewRefreshRequest.events(); return _itemViewRefreshRequest.events();
} }
void Session::requestItemPlayInline(not_null<const HistoryItem*> item) {
_itemPlayInlineRequest.fire_copy(item);
}
rpl::producer<not_null<const HistoryItem*>> Session::itemPlayInlineRequest() const {
return _itemPlayInlineRequest.events();
}
void Session::markItemRemoved(not_null<const HistoryItem*> item) { void Session::markItemRemoved(not_null<const HistoryItem*> item) {
_itemRemoved.fire_copy(item); _itemRemoved.fire_copy(item);
} }
@ -173,16 +249,9 @@ MessageIdsList Session::itemsToIds(
}) | ranges::to_vector; }) | ranges::to_vector;
} }
MessageIdsList Session::groupToIds(
not_null<HistoryMessageGroup*> group) const {
auto result = itemsToIds(group->others);
result.push_back(group->leader->fullId());
return result;
}
MessageIdsList Session::itemOrItsGroup(not_null<HistoryItem*> item) const { MessageIdsList Session::itemOrItsGroup(not_null<HistoryItem*> item) const {
if (const auto group = item->getFullGroup()) { if (const auto group = groups().find(item)) {
return groupToIds(group); return itemsToIds(group->items);
} }
return { 1, item->fullId() }; return { 1, item->fullId() };
} }

View file

@ -9,8 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/stickers.h" #include "chat_helpers/stickers.h"
#include "dialogs/dialogs_key.h" #include "dialogs/dialogs_key.h"
#include "data/data_groups.h"
struct HistoryMessageGroup; class HistoryItem;
namespace HistoryView {
struct Group;
class Element;
} // namespace HistoryView
namespace Data { namespace Data {
@ -18,6 +24,8 @@ class Feed;
class Session final { class Session final {
public: public:
using ViewElement = HistoryView::Element;
Session(); Session();
~Session(); ~Session();
@ -30,6 +38,7 @@ public:
base::Observable<void> &moreChatsLoaded() { base::Observable<void> &moreChatsLoaded() {
return _moreChatsLoaded; return _moreChatsLoaded;
} }
base::Observable<void> &pendingHistoryResize() { base::Observable<void> &pendingHistoryResize() {
return _pendingHistoryResize; return _pendingHistoryResize;
} }
@ -40,18 +49,45 @@ public:
base::Observable<ItemVisibilityQuery> &queryItemVisibility() { base::Observable<ItemVisibilityQuery> &queryItemVisibility() {
return _queryItemVisibility; return _queryItemVisibility;
} }
void markItemLayoutChanged(not_null<const HistoryItem*> item); struct IdChange {
not_null<HistoryItem*> item;
MsgId oldId = 0;
};
void registerItemView(not_null<ViewElement*> view);
void unregisterItemView(not_null<ViewElement*> view);
template <typename Method>
void enumerateItemViews(not_null<HistoryItem*> item, Method method) {
if (const auto i = _views.find(item); i != _views.end()) {
for (const auto view : i->second) {
method(view);
}
}
}
void markItemIdChange(IdChange event);
rpl::producer<IdChange> itemIdChanged() const;
void markItemLayoutChange(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemLayoutChanged() const; rpl::producer<not_null<const HistoryItem*>> itemLayoutChanged() const;
void markViewLayoutChange(not_null<const ViewElement*> view);
rpl::producer<not_null<const ViewElement*>> viewLayoutChanged() const;
void requestItemRepaint(not_null<const HistoryItem*> item); void requestItemRepaint(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemRepaintRequest() const; rpl::producer<not_null<const HistoryItem*>> itemRepaintRequest() const;
void requestViewRepaint(not_null<const ViewElement*> view);
rpl::producer<not_null<const ViewElement*>> viewRepaintRequest() const;
void requestItemViewResize(not_null<const HistoryItem*> item); void requestItemViewResize(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemViewResizeRequest() const; rpl::producer<not_null<const HistoryItem*>> itemViewResizeRequest() const;
void requestViewResize(not_null<const ViewElement*> view);
rpl::producer<not_null<const ViewElement*>> viewResizeRequest() const;
void requestItemViewRefresh(not_null<const HistoryItem*> item); void requestItemViewRefresh(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemViewRefreshRequest() const; rpl::producer<not_null<const HistoryItem*>> itemViewRefreshRequest() const;
void requestItemPlayInline(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemPlayInlineRequest() const;
void markItemRemoved(not_null<const HistoryItem*> item); void markItemRemoved(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemRemoved() const; rpl::producer<not_null<const HistoryItem*>> itemRemoved() const;
void markHistoryUnloaded(not_null<const History*> history); void markHistoryUnloaded(not_null<const History*> history);
rpl::producer<not_null<const History*>> historyUnloaded() const; rpl::producer<not_null<const History*>> historyUnloaded() const;
void markHistoryCleared(not_null<const History*> history); void markHistoryCleared(not_null<const History*> history);
rpl::producer<not_null<const History*>> historyCleared() const; rpl::producer<not_null<const History*>> historyCleared() const;
using MegagroupParticipant = std::tuple< using MegagroupParticipant = std::tuple<
@ -147,7 +183,6 @@ public:
HistoryItemsList idsToItems(const MessageIdsList &ids) const; HistoryItemsList idsToItems(const MessageIdsList &ids) const;
MessageIdsList itemsToIds(const HistoryItemsList &items) const; MessageIdsList itemsToIds(const HistoryItemsList &items) const;
MessageIdsList groupToIds(not_null<HistoryMessageGroup*> group) const;
MessageIdsList itemOrItsGroup(not_null<HistoryItem*> item) const; MessageIdsList itemOrItsGroup(not_null<HistoryItem*> item) const;
int pinnedDialogsCount() const; int pinnedDialogsCount() const;
@ -165,6 +200,13 @@ public:
void setMimeForwardIds(MessageIdsList &&list); void setMimeForwardIds(MessageIdsList &&list);
MessageIdsList takeMimeForwardIds(); MessageIdsList takeMimeForwardIds();
Groups &groups() {
return _groups;
}
const Groups &groups() const {
return _groups;
}
private: private:
bool stickersUpdateNeeded(TimeMs lastUpdate, TimeMs now) const { bool stickersUpdateNeeded(TimeMs lastUpdate, TimeMs now) const {
constexpr auto kStickersUpdateTimeout = TimeMs(3600'000); constexpr auto kStickersUpdateTimeout = TimeMs(3600'000);
@ -181,10 +223,15 @@ private:
base::Observable<void> _moreChatsLoaded; base::Observable<void> _moreChatsLoaded;
base::Observable<void> _pendingHistoryResize; base::Observable<void> _pendingHistoryResize;
base::Observable<ItemVisibilityQuery> _queryItemVisibility; base::Observable<ItemVisibilityQuery> _queryItemVisibility;
rpl::event_stream<not_null<const HistoryItem*>> _itemLayoutChanged; rpl::event_stream<IdChange> _itemIdChanges;
rpl::event_stream<not_null<const HistoryItem*>> _itemLayoutChanges;
rpl::event_stream<not_null<const ViewElement*>> _viewLayoutChanges;
rpl::event_stream<not_null<const HistoryItem*>> _itemRepaintRequest; rpl::event_stream<not_null<const HistoryItem*>> _itemRepaintRequest;
rpl::event_stream<not_null<const ViewElement*>> _viewRepaintRequest;
rpl::event_stream<not_null<const HistoryItem*>> _itemViewResizeRequest; rpl::event_stream<not_null<const HistoryItem*>> _itemViewResizeRequest;
rpl::event_stream<not_null<const ViewElement*>> _viewResizeRequest;
rpl::event_stream<not_null<const HistoryItem*>> _itemViewRefreshRequest; rpl::event_stream<not_null<const HistoryItem*>> _itemViewRefreshRequest;
rpl::event_stream<not_null<const HistoryItem*>> _itemPlayInlineRequest;
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved; rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
rpl::event_stream<not_null<const History*>> _historyUnloaded; rpl::event_stream<not_null<const History*>> _historyUnloaded;
rpl::event_stream<not_null<const History*>> _historyCleared; rpl::event_stream<not_null<const History*>> _historyCleared;
@ -207,6 +254,10 @@ private:
std::deque<Dialogs::Key> _pinnedDialogs; std::deque<Dialogs::Key> _pinnedDialogs;
base::flat_map<FeedId, std::unique_ptr<Data::Feed>> _feeds; base::flat_map<FeedId, std::unique_ptr<Data::Feed>> _feeds;
Groups _groups;
std::map<
not_null<HistoryItem*>,
std::vector<not_null<HistoryView::Element*>>> _views;
MessageIdsList _mimeForwardIds; MessageIdsList _mimeForwardIds;

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_media_types.h" #include "history/history_media_types.h"
#include "data/data_media_types.h"
#include "data/data_sparse_ids.h" #include "data/data_sparse_ids.h"
#include "info/info_memento.h" #include "info/info_memento.h"
#include "info/info_controller.h" #include "info/info_controller.h"
@ -322,10 +323,8 @@ base::optional<bool> SharedMediaWithLastSlice::IsLastIsolated(
} }
return LastFullMsgId(ending ? *ending : slice) return LastFullMsgId(ending ? *ending : slice)
| [](FullMsgId msgId) { return App::histItemById(msgId); } | [](FullMsgId msgId) { return App::histItemById(msgId); }
| [](HistoryItem *item) { return item ? item->getMedia() : nullptr; } | [](HistoryItem *item) { return item ? item->media() : nullptr; }
| [](HistoryMedia *media) { | [](Data::Media *media) { return media ? media->photo() : nullptr; }
return media ? media->getPhoto() : nullptr;
}
| [](PhotoData *photo) { return photo ? photo->id : 0; } | [](PhotoData *photo) { return photo ? photo->id : 0; }
| [&](PhotoId photoId) { return *lastPeerPhotoId != photoId; }; | [&](PhotoId photoId) { return *lastPeerPhotoId != photoId; };
} }

View file

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "base/value_ordering.h"
class HistoryItem; class HistoryItem;
using HistoryItemsList = std::vector<not_null<HistoryItem*>>; using HistoryItemsList = std::vector<not_null<HistoryItem*>>;
@ -22,6 +24,32 @@ struct UploadState {
} // namespace Data } // namespace Data
struct MessageGroupId {
using Underlying = uint64;
enum Type : Underlying {
None = 0,
} value;
MessageGroupId(Type value = None) : value(value) {
}
static MessageGroupId FromRaw(Underlying value) {
return static_cast<Type>(value);
}
explicit operator bool() const {
return value != None;
}
Underlying raw() const {
return static_cast<Underlying>(value);
}
friend inline Type value_ordering_helper(MessageGroupId value) {
return value.value;
}
};
class PeerData; class PeerData;
class UserData; class UserData;
class ChatData; class ChatData;

View file

@ -243,9 +243,10 @@ void autoplayMediaInlineAsync(const FullMsgId &msgId) {
if (auto main = App::main()) { if (auto main = App::main()) {
InvokeQueued(main, [msgId] { InvokeQueued(main, [msgId] {
if (auto item = App::histItemById(msgId)) { if (auto item = App::histItemById(msgId)) {
if (auto media = item->getMedia()) { // #TODO GIFs
media->playInline(true); //if (auto media = item->getMedia()) {
} // media->playInline(true);
//}
} }
}); });
} }

View file

@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "boxes/edit_participant_box.h" #include "boxes/edit_participant_box.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_types.h"
namespace AdminLog { namespace AdminLog {
namespace { namespace {
@ -775,7 +776,7 @@ void InnerWidget::paintEmpty(Painter &p) {
TextWithEntities InnerWidget::getSelectedText() const { TextWithEntities InnerWidget::getSelectedText() const {
return _selectedItem return _selectedItem
? _selectedItem->data()->selectedText(_selectedText) ? _selectedItem->selectedText(_selectedText)
: TextWithEntities(); : TextWithEntities();
} }
@ -911,7 +912,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
const auto item = view ? view->data().get() : nullptr; const auto item = view ? view->data().get() : nullptr;
const auto itemId = item ? item->fullId() : FullMsgId(); const auto itemId = item ? item->fullId() : FullMsgId();
bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg()); bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg());
bool canForward = item && item->canForward(); bool canForward = item && item->allowsForward();
auto msg = dynamic_cast<HistoryMessage*>(item); auto msg = dynamic_cast<HistoryMessage*>(item);
if (isUponSelected > 0) { if (isUponSelected > 0) {
@ -919,7 +920,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
} else { } else {
if (item && !isUponSelected) { if (item && !isUponSelected) {
auto mediaHasTextForCopy = false; auto mediaHasTextForCopy = false;
if (auto media = (msg ? msg->getMedia() : nullptr)) { if (auto media = view->media()) {
mediaHasTextForCopy = media->hasTextForCopy(); mediaHasTextForCopy = media->hasTextForCopy();
if (media->type() == MediaTypeWebPage && static_cast<HistoryWebPage*>(media)->attach()) { if (media->type() == MediaTypeWebPage && static_cast<HistoryWebPage*>(media)->attach()) {
media = static_cast<HistoryWebPage*>(media)->attach(); media = static_cast<HistoryWebPage*>(media)->attach();
@ -1040,8 +1041,8 @@ void InnerWidget::showContextInFolder(not_null<DocumentData*> document) {
void InnerWidget::openContextGif(FullMsgId itemId) { void InnerWidget::openContextGif(FullMsgId itemId) {
if (const auto item = App::histItemById(itemId)) { if (const auto item = App::histItemById(itemId)) {
if (auto media = item->getMedia()) { if (auto media = item->media()) {
if (auto document = media->getDocument()) { if (auto document = media->document()) {
Messenger::Instance().showDocument(document, item); Messenger::Instance().showDocument(document, item);
} }
} }
@ -1050,13 +1051,10 @@ void InnerWidget::openContextGif(FullMsgId itemId) {
void InnerWidget::copyContextText(FullMsgId itemId) { void InnerWidget::copyContextText(FullMsgId itemId) {
if (const auto item = App::histItemById(itemId)) { if (const auto item = App::histItemById(itemId)) {
if (const auto media = item->getMedia()) { if (const auto view = viewForItem(item)) {
if (media->type() == MediaTypeSticker) { setToClipboard(view->selectedText(FullSelection));
return;
} }
} }
setToClipboard(item->selectedText(FullSelection));
}
} }
void InnerWidget::setToClipboard( void InnerWidget::setToClipboard(
@ -1416,7 +1414,7 @@ void InnerWidget::updateSelected() {
} }
auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) }; auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) };
if (_mouseSelectType != TextSelectType::Letters) { if (_mouseSelectType != TextSelectType::Letters) {
selection = _mouseActionItem->data()->adjustSelection( selection = _mouseActionItem->adjustSelection(
selection, selection,
_mouseSelectType); _mouseSelectType);
} }
@ -1517,17 +1515,21 @@ void InnerWidget::performDrag() {
// auto forwardMimeType = QString(); // auto forwardMimeType = QString();
// auto pressedMedia = static_cast<HistoryMedia*>(nullptr); // auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
// if (auto pressedItem = App::pressedItem()) { // if (auto pressedItem = App::pressedItem()) {
// pressedMedia = pressedItem->getMedia(); // pressedMedia = pressedItem->media();
// if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) { // if (_mouseCursorState == HistoryInDateCursorState
// || (pressedMedia && pressedMedia->dragItem())) {
// forwardMimeType = qsl("application/x-td-forward"); // forwardMimeType = qsl("application/x-td-forward");
// Auth().data().setMimeForwardIds(Auth().data().itemOrItsGroup(pressedItem)); // Auth().data().setMimeForwardIds(
// Auth().data().itemOrItsGroup(pressedItem->data()));
// } // }
// } // }
// if (auto pressedLnkItem = App::pressedLinkItem()) { // if (auto pressedLnkItem = App::pressedLinkItem()) {
// if ((pressedMedia = pressedLnkItem->getMedia())) { // if ((pressedMedia = pressedLnkItem->media())) {
// if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) { // if (forwardMimeType.isEmpty()
// && pressedMedia->dragItemByHandler(pressedHandler)) {
// forwardMimeType = qsl("application/x-td-forward"); // forwardMimeType = qsl("application/x-td-forward");
// Auth().data().setMimeForwardIds({ 1, pressedLnkItem->fullId() }); // Auth().data().setMimeForwardIds(
// { 1, pressedLnkItem->fullId() });
// } // }
// } // }
// } // }

View file

@ -422,9 +422,13 @@ void GenerateItems(
auto text = lng_admin_log_pinned_message(lt_from, fromLinkText); auto text = lng_admin_log_pinned_message(lt_from, fromLinkText);
addSimpleServiceMessage(text); addSimpleServiceMessage(text);
auto applyServiceAction = false;
auto detachExistingItem = false; auto detachExistingItem = false;
addPart(history->createItem(PrepareLogMessage(action.vmessage, idManager.next(), date.v), applyServiceAction, detachExistingItem)); addPart(history->createItem(
PrepareLogMessage(
action.vmessage,
idManager.next(),
date.v),
detachExistingItem));
} }
}; };
@ -438,9 +442,13 @@ void GenerateItems(
addSimpleServiceMessage(text); addSimpleServiceMessage(text);
auto oldValue = ExtractEditedText(action.vprev_message); auto oldValue = ExtractEditedText(action.vprev_message);
auto applyServiceAction = false;
auto detachExistingItem = false; auto detachExistingItem = false;
auto body = history->createItem(PrepareLogMessage(action.vnew_message, idManager.next(), date.v), applyServiceAction, detachExistingItem); auto body = history->createItem(
PrepareLogMessage(
action.vnew_message,
idManager.next(),
date.v),
detachExistingItem);
if (oldValue.text.isEmpty()) { if (oldValue.text.isEmpty()) {
oldValue = PrepareText(QString(), lang(lng_admin_log_empty_text)); oldValue = PrepareText(QString(), lang(lng_admin_log_empty_text));
} }
@ -452,9 +460,10 @@ void GenerateItems(
auto text = lng_admin_log_deleted_message(lt_from, fromLinkText); auto text = lng_admin_log_deleted_message(lt_from, fromLinkText);
addSimpleServiceMessage(text); addSimpleServiceMessage(text);
auto applyServiceAction = false;
auto detachExistingItem = false; auto detachExistingItem = false;
addPart(history->createItem(PrepareLogMessage(action.vmessage, idManager.next(), date.v), applyServiceAction, detachExistingItem)); addPart(history->createItem(
PrepareLogMessage(action.vmessage, idManager.next(), date.v),
detachExistingItem));
}; };
auto createParticipantJoin = [&]() { auto createParticipantJoin = [&]() {

File diff suppressed because it is too large Load diff

View file

@ -164,7 +164,9 @@ public:
not_null<HistoryItem*> addNewGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup); not_null<HistoryItem*> addNewGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup);
// Used only internally and for channel admin log. // Used only internally and for channel admin log.
HistoryItem *createItem(const MTPMessage &msg, bool applyServiceAction, bool detachExistingItem); HistoryItem *createItem(
const MTPMessage &message,
bool detachExistingItem);
void addOlderSlice(const QVector<MTPMessage> &slice); void addOlderSlice(const QVector<MTPMessage> &slice);
void addNewerSlice(const QVector<MTPMessage> &slice); void addNewerSlice(const QVector<MTPMessage> &slice);
@ -400,9 +402,11 @@ protected:
not_null<HistoryItem*> createItemPhoto(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup); not_null<HistoryItem*> createItemPhoto(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup);
not_null<HistoryItem*> createItemGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup); not_null<HistoryItem*> createItemGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup);
not_null<HistoryItem*> addNewItem(not_null<HistoryItem*> adding, bool newMsg); not_null<HistoryItem*> addNewItem(
not_null<HistoryItem*> item,
bool unread);
not_null<HistoryItem*> addNewInTheMiddle( not_null<HistoryItem*> addNewInTheMiddle(
not_null<HistoryItem*> newItem, not_null<HistoryItem*> item,
int blockIndex, int blockIndex,
int itemIndex); int itemIndex);
@ -429,6 +433,13 @@ private:
void changedInChatListHook(Dialogs::Mode list, bool added) override; void changedInChatListHook(Dialogs::Mode list, bool added) override;
void changedChatListPinHook() override; void changedChatListPinHook() override;
void applyMessageChanges(
not_null<HistoryItem*> item,
const MTPMessage &original);
void applyServiceChanges(
not_null<HistoryItem*> item,
const MTPDmessageService &data);
// After adding a new history slice check the lastMsg and newLoaded. // After adding a new history slice check the lastMsg and newLoaded.
void checkLastMsg(); void checkLastMsg();
@ -441,17 +452,19 @@ private:
void clearSendAction(not_null<UserData*> from); void clearSendAction(not_null<UserData*> from);
HistoryItem *findPreviousItem(not_null<HistoryItem*> item) const; HistoryView::Element *findPreviousItem(
HistoryItem *findNextItem(not_null<HistoryItem*> item) const; not_null<HistoryView::Element*> view) const;
not_null<HistoryItem*> findGroupFirst( HistoryView::Element *findNextItem(
not_null<HistoryItem*> item) const; not_null<HistoryView::Element*> view) const;
not_null<HistoryItem*> findGroupLast( not_null<HistoryView::Element*> findGroupFirst(
not_null<HistoryItem*> item) const; not_null<HistoryView::Element*> view) const;
auto recountGroupingFromTill(not_null<HistoryItem*> item) not_null<HistoryView::Element*> findGroupLast(
-> std::pair<not_null<HistoryItem*>, not_null<HistoryItem*>>; not_null<HistoryView::Element*> view) const;
auto recountGroupingFromTill(not_null<HistoryView::Element*> view)
-> std::pair<not_null<HistoryView::Element*>, not_null<HistoryView::Element*>>;
void recountGrouping( void recountGrouping(
not_null<HistoryItem*> from, not_null<HistoryView::Element*> from,
not_null<HistoryItem*> till); not_null<HistoryView::Element*> till);
enum class Flag { enum class Flag {
f_has_pending_resized_items = (1 << 0), f_has_pending_resized_items = (1 << 0),

View file

@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" #include "apiwrap.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_types.h"
namespace { namespace {
@ -405,7 +406,7 @@ void HistoryInner::enumerateDates(Method method) {
TextSelection HistoryInner::computeRenderSelection( TextSelection HistoryInner::computeRenderSelection(
not_null<const SelectedItems*> selected, not_null<const SelectedItems*> selected,
not_null<HistoryItem*> item) const { not_null<Element*> view) const {
const auto itemSelection = [&](not_null<HistoryItem*> item) { const auto itemSelection = [&](not_null<HistoryItem*> item) {
auto i = selected->find(item); auto i = selected->find(item);
if (i != selected->end()) { if (i != selected->end()) {
@ -413,22 +414,22 @@ TextSelection HistoryInner::computeRenderSelection(
} }
return TextSelection(); return TextSelection();
}; };
const auto group = item->Get<HistoryMessageGroup>(); const auto group = view->Get<HistoryView::Group>();
if (group) { if (group) {
if (group->leader != item) { if (group->leader != view) {
return TextSelection(); return TextSelection();
} }
auto result = TextSelection(); auto result = TextSelection();
auto allFullSelected = true; auto allFullSelected = true;
const auto count = int(group->others.size()); const auto count = int(group->others.size());
for (auto i = 0; i != count; ++i) { for (auto i = 0; i != count; ++i) {
if (itemSelection(group->others[i]) == FullSelection) { if (itemSelection(group->others[i]->data()) == FullSelection) {
result = AddGroupItemSelection(result, i); result = AddGroupItemSelection(result, i);
} else { } else {
allFullSelected = false; allFullSelected = false;
} }
} }
const auto leaderSelection = itemSelection(item); const auto leaderSelection = itemSelection(view->data());
if (leaderSelection == FullSelection) { if (leaderSelection == FullSelection) {
return allFullSelected return allFullSelected
? FullSelection ? FullSelection
@ -438,7 +439,7 @@ TextSelection HistoryInner::computeRenderSelection(
} }
return result; return result;
} }
return itemSelection(item); return itemSelection(view->data());
} }
TextSelection HistoryInner::itemRenderSelection( TextSelection HistoryInner::itemRenderSelection(
@ -452,7 +453,7 @@ TextSelection HistoryInner::itemRenderSelection(
return FullSelection; return FullSelection;
} }
} else if (!_selected.empty()) { } else if (!_selected.empty()) {
return computeRenderSelection(&_selected, item); return computeRenderSelection(&_selected, view);
} }
return TextSelection(); return TextSelection();
} }
@ -528,7 +529,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
} }
if (item->mentionsMe() && item->isMediaUnread()) { if (item->mentionsMe() && item->isMediaUnread()) {
readMentions.insert(item); readMentions.insert(item);
_widget->enqueueMessageHighlight(item); _widget->enqueueMessageHighlight(view);
} }
int32 h = view->height(); int32 h = view->height();
@ -574,7 +575,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
} }
if (item->mentionsMe() && item->isMediaUnread()) { if (item->mentionsMe() && item->isMediaUnread()) {
readMentions.insert(item); readMentions.insert(item);
_widget->enqueueMessageHighlight(item); _widget->enqueueMessageHighlight(view);
} }
} }
p.translate(0, h); p.translate(0, h);
@ -984,7 +985,7 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but
if (uponSelected) { if (uponSelected) {
_mouseAction = MouseAction::PrepareDrag; // start text drag _mouseAction = MouseAction::PrepareDrag; // start text drag
} else if (!_pressWasInactive) { } else if (!_pressWasInactive) {
if (dynamic_cast<HistorySticker*>(App::pressedItem()->data()->getMedia()) || _mouseCursorState == HistoryInDateCursorState) { if (dynamic_cast<HistorySticker*>(App::pressedItem()->media()) || _mouseCursorState == HistoryInDateCursorState) {
_mouseAction = MouseAction::PrepareDrag; // start sticker drag or by-date drag _mouseAction = MouseAction::PrepareDrag; // start sticker drag or by-date drag
} else { } else {
if (dragState.afterSymbol) ++_mouseTextSymbol; if (dragState.afterSymbol) ++_mouseTextSymbol;
@ -1089,20 +1090,26 @@ void HistoryInner::performDrag() {
auto forwardMimeType = QString(); auto forwardMimeType = QString();
auto pressedMedia = static_cast<HistoryMedia*>(nullptr); auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
if (auto pressedItem = App::pressedItem()) { if (auto pressedItem = App::pressedItem()) {
pressedMedia = pressedItem->data()->getMedia(); pressedMedia = pressedItem->media();
if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) { if (_mouseCursorState == HistoryInDateCursorState
Auth().data().setMimeForwardIds(Auth().data().itemOrItsGroup(pressedItem->data())); || (pressedMedia && pressedMedia->dragItem())) {
Auth().data().setMimeForwardIds(
Auth().data().itemOrItsGroup(pressedItem->data()));
forwardMimeType = qsl("application/x-td-forward"); forwardMimeType = qsl("application/x-td-forward");
} }
} }
if (const auto pressedLnkItem = _mouseActionItem) { if (const auto pressedLnkItem = _mouseActionItem) {
if ((pressedMedia = pressedLnkItem->getMedia())) { if (const auto view = pressedLnkItem->mainView()) {
if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) { if ((pressedMedia = view->media())) {
Auth().data().setMimeForwardIds({ 1, pressedLnkItem->fullId() }); if (forwardMimeType.isEmpty()
&& pressedMedia->dragItemByHandler(pressedHandler)) {
Auth().data().setMimeForwardIds(
{ 1, pressedLnkItem->fullId() });
forwardMimeType = qsl("application/x-td-forward"); forwardMimeType = qsl("application/x-td-forward");
} }
} }
} }
}
if (!forwardMimeType.isEmpty()) { if (!forwardMimeType.isEmpty()) {
auto mimeData = std::make_unique<QMimeData>(); auto mimeData = std::make_unique<QMimeData>();
mimeData->setData(forwardMimeType, "1"); mimeData->setData(forwardMimeType, "1");
@ -1162,13 +1169,15 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu
// if we are in selecting items mode perhaps we want to // if we are in selecting items mode perhaps we want to
// toggle selection instead of activating the pressed link // toggle selection instead of activating the pressed link
if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) { if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) {
if (auto media = _mouseActionItem->getMedia()) { if (const auto view = _mouseActionItem->mainView()) {
if (const auto media = view->media()) {
if (media->toggleSelectionByHandlerClick(activated)) { if (media->toggleSelectionByHandlerClick(activated)) {
activated = nullptr; activated = nullptr;
} }
} }
} }
} }
}
if (App::pressedItem()) { if (App::pressedItem()) {
repaintItem(App::pressedItem()); repaintItem(App::pressedItem());
App::pressedItem(nullptr); App::pressedItem(nullptr);
@ -1232,7 +1241,11 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu
#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 #if defined Q_OS_LINUX32 || defined Q_OS_LINUX64
if (!_selected.empty() && _selected.cbegin()->second != FullSelection) { if (!_selected.empty() && _selected.cbegin()->second != FullSelection) {
const auto [item, selection] = *_selected.cbegin(); const auto [item, selection] = *_selected.cbegin();
setToClipboard(item->selectedText(selection), QClipboard::Selection); if (const auto view = item->mainView()) {
setToClipboard(
view->selectedText(selection),
QClipboard::Selection);
}
} }
#endif // Q_OS_LINUX32 || Q_OS_LINUX64 #endif // Q_OS_LINUX32 || Q_OS_LINUX64
} }
@ -1340,7 +1353,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
_widget->replyToMessage(itemId); _widget->replyToMessage(itemId);
}); });
} }
if (item->canEdit(::date(unixtime()))) { if (item->allowsEdit(::date(unixtime()))) {
_menu->addAction(lang(lng_context_edit_msg), [=] { _menu->addAction(lang(lng_context_edit_msg), [=] {
_widget->editMessage(itemId); _widget->editMessage(itemId);
}); });
@ -1423,7 +1436,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
} else if (item) { } else if (item) {
const auto itemId = item->fullId(); const auto itemId = item->fullId();
if (isUponSelected != -2) { if (isUponSelected != -2) {
if (item->canForward()) { if (item->allowsForward()) {
_menu->addAction(lang(lng_context_forward_msg), [=] { _menu->addAction(lang(lng_context_forward_msg), [=] {
forwardItem(itemId); forwardItem(itemId);
})->setEnabled(true); })->setEnabled(true);
@ -1453,10 +1466,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
: App::hoveredLinkItem() : App::hoveredLinkItem()
? App::hoveredLinkItem()->data().get() ? App::hoveredLinkItem()->data().get()
: nullptr) { : nullptr) {
if (const auto group = result->getFullGroup()) { if (const auto group = Auth().data().groups().find(result)) {
return group->others.empty() return group->items.front();
? group->leader
: group->others.front().get();
} }
return result; return result;
} }
@ -1466,8 +1477,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
const auto canDelete = item const auto canDelete = item
&& item->canDelete() && item->canDelete()
&& (item->id > 0 || !item->serviceMsg()); && (item->id > 0 || !item->serviceMsg());
const auto canForward = item const auto canForward = item && item->allowsForward();
&& item->canForward(); const auto view = item ? item->mainView() : nullptr;
const auto msg = dynamic_cast<HistoryMessage*>(item); const auto msg = dynamic_cast<HistoryMessage*>(item);
if (isUponSelected > 0) { if (isUponSelected > 0) {
@ -1477,7 +1488,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
addItemActions(item); addItemActions(item);
if (item && !isUponSelected) { if (item && !isUponSelected) {
auto mediaHasTextForCopy = false; auto mediaHasTextForCopy = false;
if (auto media = (msg ? msg->getMedia() : nullptr)) { if (auto media = (view ? view->media() : nullptr)) {
mediaHasTextForCopy = media->hasTextForCopy(); mediaHasTextForCopy = media->hasTextForCopy();
if (media->type() == MediaTypeWebPage && static_cast<HistoryWebPage*>(media)->attach()) { if (media->type() == MediaTypeWebPage && static_cast<HistoryWebPage*>(media)->attach()) {
media = static_cast<HistoryWebPage*>(media)->attach(); media = static_cast<HistoryWebPage*>(media)->attach();
@ -1665,8 +1676,8 @@ void HistoryInner::saveDocumentToFile(not_null<DocumentData*> document) {
void HistoryInner::openContextGif(FullMsgId itemId) { void HistoryInner::openContextGif(FullMsgId itemId) {
if (const auto item = App::histItemById(itemId)) { if (const auto item = App::histItemById(itemId)) {
if (const auto media = item->getMedia()) { if (const auto media = item->media()) {
if (const auto document = media->getDocument()) { if (const auto document = media->document()) {
Messenger::Instance().showDocument(document, item); Messenger::Instance().showDocument(document, item);
} }
} }
@ -1675,8 +1686,8 @@ void HistoryInner::openContextGif(FullMsgId itemId) {
void HistoryInner::saveContextGif(FullMsgId itemId) { void HistoryInner::saveContextGif(FullMsgId itemId) {
if (const auto item = App::histItemById(itemId)) { if (const auto item = App::histItemById(itemId)) {
if (const auto media = item->getMedia()) { if (const auto media = item->media()) {
if (const auto document = media->getDocument()) { if (const auto document = media->document()) {
_widget->saveGif(document); _widget->saveGif(document);
} }
} }
@ -1685,15 +1696,12 @@ void HistoryInner::saveContextGif(FullMsgId itemId) {
void HistoryInner::copyContextText(FullMsgId itemId) { void HistoryInner::copyContextText(FullMsgId itemId) {
if (const auto item = App::histItemById(itemId)) { if (const auto item = App::histItemById(itemId)) {
if (const auto media = item->getMedia()) { if (const auto view = item->mainView()) {
if (media->type() == MediaTypeSticker) { const auto group = view->getFullGroup();
return; const auto leader = group ? group->leader : view;
}
}
const auto group = item->getFullGroup();
const auto leader = group ? group->leader : item;
setToClipboard(leader->selectedText(FullSelection)); setToClipboard(leader->selectedText(FullSelection));
} }
}
} }
void HistoryInner::setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode) { void HistoryInner::setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode) {
@ -1718,26 +1726,30 @@ TextWithEntities HistoryInner::getSelectedText() const {
} }
if (selected.cbegin()->second != FullSelection) { if (selected.cbegin()->second != FullSelection) {
const auto [item, selection] = *selected.cbegin(); const auto [item, selection] = *selected.cbegin();
return item->selectedText(selection); if (const auto view = item->mainView()) {
return view->selectedText(selection);
}
return TextWithEntities();
} }
const auto timeFormat = qsl(", [dd.MM.yy hh:mm]\n"); const auto timeFormat = qsl(", [dd.MM.yy hh:mm]\n");
auto groupLeadersAdded = base::flat_set<not_null<HistoryItem*>>(); auto groupLeadersAdded = base::flat_set<not_null<Element*>>();
auto fullSize = 0; auto fullSize = 0;
auto texts = base::flat_map<std::pair<int, MsgId>, TextWithEntities>(); auto texts = base::flat_map<std::pair<int, MsgId>, TextWithEntities>();
const auto addItem = [&]( const auto addItem = [&](
not_null<HistoryItem*> item, not_null<HistoryView::Element*> view,
TextSelection selection) { TextSelection selection) {
const auto item = view->data();
auto time = item->date.toString(timeFormat); auto time = item->date.toString(timeFormat);
auto part = TextWithEntities(); auto part = TextWithEntities();
auto unwrapped = item->selectedText(selection); auto unwrapped = view->selectedText(selection);
auto size = item->author()->name.size() auto size = item->author()->name.size()
+ time.size() + time.size()
+ unwrapped.text.size(); + unwrapped.text.size();
part.text.reserve(size); part.text.reserve(size);
auto y = itemTop(item); auto y = itemTop(view);
if (y >= 0) { if (y >= 0) {
part.text.append(item->author()->name).append(time); part.text.append(item->author()->name).append(time);
TextUtilities::Append(part, std::move(unwrapped)); TextUtilities::Append(part, std::move(unwrapped));
@ -1747,11 +1759,12 @@ TextWithEntities HistoryInner::getSelectedText() const {
}; };
for (const auto [item, selection] : selected) { for (const auto [item, selection] : selected) {
if (!item->mainView()) { const auto view = item->mainView();
if (!view) {
continue; continue;
} }
if (const auto group = item->Get<HistoryMessageGroup>()) { if (const auto group = view->getFullGroup()) {
if (groupLeadersAdded.contains(group->leader)) { if (groupLeadersAdded.contains(group->leader)) {
continue; continue;
} }
@ -1761,16 +1774,16 @@ TextWithEntities HistoryInner::getSelectedText() const {
if (leaderSelection == FullSelection) { if (leaderSelection == FullSelection) {
groupLeadersAdded.emplace(group->leader); groupLeadersAdded.emplace(group->leader);
addItem(group->leader, FullSelection); addItem(group->leader, FullSelection);
} else if (item == group->leader) { } else if (view == group->leader) {
const auto leaderFullSelection = AddGroupItemSelection( const auto leaderFullSelection = AddGroupItemSelection(
TextSelection(), TextSelection(),
int(group->others.size())); int(group->others.size()));
addItem(item, leaderFullSelection); addItem(view, leaderFullSelection);
} else { } else {
addItem(item, FullSelection); addItem(view, FullSelection);
} }
} else { } else {
addItem(item, FullSelection); addItem(view, FullSelection);
} }
} }
@ -2174,7 +2187,7 @@ auto HistoryInner::getSelectionState() const
if (selected.first->canDelete()) { if (selected.first->canDelete()) {
++result.canDeleteCount; ++result.canDeleteCount;
} }
if (selected.first->canForward()) { if (selected.first->allowsForward()) {
++result.canForwardCount; ++result.canForwardCount;
} }
} else { } else {
@ -2411,7 +2424,9 @@ void HistoryInner::onUpdateSelected() {
} }
auto selState = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) }; auto selState = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) };
if (_mouseSelectType != TextSelectType::Letters) { if (_mouseSelectType != TextSelectType::Letters) {
selState = _mouseActionItem->adjustSelection(selState, _mouseSelectType); if (const auto view = _mouseActionItem->mainView()) {
selState = view->adjustSelection(selState, _mouseSelectType);
}
} }
if (_selected[_mouseActionItem] != selState) { if (_selected[_mouseActionItem] != selState) {
_selected[_mouseActionItem] = selState; _selected[_mouseActionItem] = selState;
@ -2570,7 +2585,7 @@ int HistoryInner::itemTop(const HistoryItem *item) const {
} }
int HistoryInner::itemTop(const Element *view) const { int HistoryInner::itemTop(const Element *view) const {
if (!view) { if (!view || view->data()->mainView() != view) {
return -1; return -1;
} }
@ -2609,12 +2624,14 @@ int HistoryInner::moveScrollFollowingInlineKeyboard(
const HistoryItem *item, const HistoryItem *item,
int oldKeyboardTop, int oldKeyboardTop,
int newKeyboardTop) { int newKeyboardTop) {
if (item->isUnderCursor()) { if (const auto view = item ? item->mainView() : nullptr) {
if (view->isUnderCursor()) {
const auto top = itemTop(item); const auto top = itemTop(item);
if (top >= oldKeyboardTop) { if (top >= oldKeyboardTop) {
return newKeyboardTop - oldKeyboardTop; return newKeyboardTop - oldKeyboardTop;
} }
} }
}
return 0; return 0;
} }
@ -2632,11 +2649,8 @@ bool HistoryInner::isSelected(
bool HistoryInner::isSelectedAsGroup( bool HistoryInner::isSelectedAsGroup(
not_null<SelectedItems*> toItems, not_null<SelectedItems*> toItems,
not_null<HistoryItem*> item) const { not_null<HistoryItem*> item) const {
if (const auto group = item->getFullGroup()) { if (const auto group = Auth().data().groups().find(item)) {
if (!isSelected(toItems, group->leader)) { for (const auto other : group->items) {
return false;
}
for (const auto other : group->others) {
if (!isSelected(toItems, other)) { if (!isSelected(toItems, other)) {
return false; return false;
} }
@ -2703,7 +2717,7 @@ void HistoryInner::changeSelectionAsGroup(
not_null<SelectedItems*> toItems, not_null<SelectedItems*> toItems,
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
SelectAction action) const { SelectAction action) const {
const auto group = item->getFullGroup(); const auto group = Auth().data().groups().find(item);
if (!group) { if (!group) {
return changeSelection(toItems, item, action); return changeSelection(toItems, item, action);
} }
@ -2716,10 +2730,7 @@ void HistoryInner::changeSelectionAsGroup(
const auto add = (action == SelectAction::Select); const auto add = (action == SelectAction::Select);
const auto adding = [&] { const auto adding = [&] {
if (!add || !goodForSelection(toItems, group->leader, total)) { for (const auto other : group->items) {
return false;
}
for (const auto other : group->others) {
if (!goodForSelection(toItems, other, total)) { if (!goodForSelection(toItems, other, total)) {
return false; return false;
} }
@ -2727,13 +2738,11 @@ void HistoryInner::changeSelectionAsGroup(
return (total <= MaxSelectedItems); return (total <= MaxSelectedItems);
}(); }();
if (adding) { if (adding) {
addToSelection(toItems, group->leader); for (const auto other : group->items) {
for (const auto other : group->others) {
addToSelection(toItems, other); addToSelection(toItems, other);
} }
} else { } else {
removeFromSelection(toItems, group->leader); for (const auto other : group->items) {
for (const auto other : group->others) {
removeFromSelection(toItems, other); removeFromSelection(toItems, other);
} }
} }
@ -2745,13 +2754,7 @@ void HistoryInner::forwardItem(FullMsgId itemId) {
void HistoryInner::forwardAsGroup(FullMsgId itemId) { void HistoryInner::forwardAsGroup(FullMsgId itemId) {
if (const auto item = App::histItemById(itemId)) { if (const auto item = App::histItemById(itemId)) {
if (const auto group = item->getFullGroup()) { Window::ShowForwardMessagesBox(Auth().data().itemOrItsGroup(item));
auto items = Auth().data().itemsToIds(group->others);
items.push_back(group->leader->fullId());
Window::ShowForwardMessagesBox(std::move(items));
} else {
Window::ShowForwardMessagesBox({ 1, itemId });
}
} }
} }
@ -2779,13 +2782,12 @@ bool HistoryInner::hasPendingResizedItems() const {
void HistoryInner::deleteAsGroup(FullMsgId itemId) { void HistoryInner::deleteAsGroup(FullMsgId itemId) {
if (const auto item = App::histItemById(itemId)) { if (const auto item = App::histItemById(itemId)) {
const auto group = item->getFullGroup(); const auto group = Auth().data().groups().find(item);
if (!group || group->others.empty()) { if (!group || group->items.size() < 2) {
return deleteItem(item); return deleteItem(item);
} }
auto items = Auth().data().itemsToIds(group->others); Ui::show(Box<DeleteMessagesBox>(
items.push_back(group->leader->fullId()); Auth().data().itemsToIds(group->items)));
Ui::show(Box<DeleteMessagesBox>(Auth().data().groupToIds(group)));
} }
} }
@ -2866,7 +2868,7 @@ QString HistoryInner::tooltipText() const {
if (const auto view = App::hoveredItem()) { if (const auto view = App::hoveredItem()) {
auto dateText = view->data()->date.toString( auto dateText = view->data()->date.toString(
QLocale::system().dateTimeFormat(QLocale::LongFormat)); QLocale::system().dateTimeFormat(QLocale::LongFormat));
auto editedDate = view->data()->displayedEditDate(); auto editedDate = view->displayedEditDate();
if (!editedDate.isNull()) { if (!editedDate.isNull()) {
dateText += '\n' + lng_edited_date(lt_date, editedDate.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat))); dateText += '\n' + lng_edited_date(lt_date, editedDate.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)));
} }

View file

@ -212,7 +212,7 @@ private:
int seltoy) const; int seltoy) const;
TextSelection computeRenderSelection( TextSelection computeRenderSelection(
not_null<const SelectedItems*> selected, not_null<const SelectedItems*> selected,
not_null<HistoryItem*> item) const; not_null<Element*> view) const;
void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard); void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard);

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_components.h" #include "history/history_item_components.h"
#include "history/history_media_types.h" #include "history/history_media_types.h"
#include "history/history_media_grouped.h" #include "history/history_media_grouped.h"
#include "history/history_service.h"
#include "history/history_message.h" #include "history/history_message.h"
#include "history/history.h" #include "history/history.h"
#include "media/media_clip_reader.h" #include "media/media_clip_reader.h"
@ -34,25 +35,40 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/crash_reports.h" #include "core/crash_reports.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_messages.h" #include "data/data_messages.h"
#include "data/data_media_types.h"
#include "data/data_feed.h" #include "data/data_feed.h"
namespace internal { namespace {
TextSelection unshiftSelection(TextSelection selection, uint16 byLength) { not_null<HistoryItem*> CreateUnsupportedMessage(
if (selection == FullSelection) { not_null<History*> history,
return selection; MsgId msgId,
} MTPDmessage::Flags flags,
return ::unshiftSelection(selection, byLength); MsgId replyTo,
UserId viaBotId,
QDateTime date,
UserId from) {
const auto siteLink = qsl("https://desktop.telegram.org");
auto text = TextWithEntities{
lng_message_unsupported(lt_link, siteLink)
};
TextUtilities::ParseEntities(text, Ui::ItemTextNoMonoOptions().flags);
text.entities.push_front(
EntityInText(EntityInTextItalic, 0, text.text.size()));
flags &= ~MTPDmessage::Flag::f_post_author;
return HistoryMessage::create(
history,
msgId,
flags,
replyTo,
viaBotId,
date,
from,
QString(),
text);
} }
TextSelection shiftSelection(TextSelection selection, uint16 byLength) { } // namespace
if (selection == FullSelection) {
return selection;
}
return ::shiftSelection(selection, byLength);
}
} // namespace internal
HistoryItem::HistoryItem( HistoryItem::HistoryItem(
not_null<History*> history, not_null<History*> history,
@ -93,6 +109,12 @@ void HistoryItem::finishEdition(int oldKeyboardTop) {
App::historyUpdateDependent(this); App::historyUpdateDependent(this);
} }
void HistoryItem::setGroupId(MessageGroupId groupId) {
Auth().data().groups().unregisterMessage(this);
_groupId = groupId;
Auth().data().groups().registerMessage(this);
}
HistoryMessageReplyMarkup *HistoryItem::inlineReplyMarkup() { HistoryMessageReplyMarkup *HistoryItem::inlineReplyMarkup() {
if (const auto markup = Get<HistoryMessageReplyMarkup>()) { if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_inline) { if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_inline) {
@ -189,12 +211,17 @@ MTPDreplyKeyboardMarkup::Flags HistoryItem::replyKeyboardFlags() const {
return MTPDreplyKeyboardMarkup_ClientFlag::f_zero | 0; return MTPDreplyKeyboardMarkup_ClientFlag::f_zero | 0;
} }
void HistoryItem::addLogEntryOriginal(WebPageId localId, const QString &label, const TextWithEntities &content) { void HistoryItem::addLogEntryOriginal(
WebPageId localId,
const QString &label,
const TextWithEntities &content) {
Expects(isLogEntry()); Expects(isLogEntry());
AddComponents(HistoryMessageLogEntryOriginal::Bit()); AddComponents(HistoryMessageLogEntryOriginal::Bit());
auto original = Get<HistoryMessageLogEntryOriginal>(); Get<HistoryMessageLogEntryOriginal>()->page = App::feedWebPage(
auto webpage = App::feedWebPage(localId, label, content); localId,
original->_page = std::make_unique<HistoryWebPage>(this, webpage); label,
content);
} }
UserData *HistoryItem::viaBot() const { UserData *HistoryItem::viaBot() const {
@ -276,20 +303,11 @@ void HistoryItem::removeMainView() {
void HistoryItem::clearMainView() { void HistoryItem::clearMainView() {
_mainView = nullptr; _mainView = nullptr;
validateGroupId();
if (groupId()) {
makeGroupLeader({});
}
} }
void HistoryItem::addToUnreadMentions(UnreadMentionType type) { void HistoryItem::addToUnreadMentions(UnreadMentionType type) {
} }
Storage::SharedMediaTypesMask HistoryItem::sharedMediaTypes() const {
return {};
}
void HistoryItem::indexAsNewItem() { void HistoryItem::indexAsNewItem() {
if (IsServerMsgId(id)) { if (IsServerMsgId(id)) {
CrashReports::SetAnnotation("addToUnreadMentions", QString::number(id)); CrashReports::SetAnnotation("addToUnreadMentions", QString::number(id));
@ -307,27 +325,21 @@ void HistoryItem::indexAsNewItem() {
void HistoryItem::setRealId(MsgId newId) { void HistoryItem::setRealId(MsgId newId) {
Expects(!IsServerMsgId(id)); Expects(!IsServerMsgId(id));
id = newId; App::historyUnregItem(this);
const auto oldId = std::exchange(id, newId);
App::historyRegItem(this);
// We don't need to call Notify::replyMarkupUpdated(this) and update keyboard // We don't need to call Notify::replyMarkupUpdated(this) and update keyboard
// in history widget, because it can't exist for an outgoing message. // in history widget, because it can't exist for an outgoing message.
// Only inline keyboards can be in outgoing messages. // Only inline keyboards can be in outgoing messages.
if (auto markup = inlineReplyMarkup()) { if (const auto markup = inlineReplyMarkup()) {
if (markup->inlineKeyboard) { if (markup->inlineKeyboard) {
markup->inlineKeyboard->updateMessageId(); markup->inlineKeyboard->updateMessageId();
} }
} }
if (_media) { Auth().data().markItemIdChange({ this, oldId });
_media->refreshParentId(this); Auth().data().requestItemRepaint(this);
if (const auto group = Get<HistoryMessageGroup>()) {
if (group->leader != this) {
if (const auto media = group->leader->getMedia()) {
media->refreshParentId(group->leader);
}
}
}
}
} }
bool HistoryItem::isPinned() const { bool HistoryItem::isPinned() const {
@ -347,60 +359,11 @@ bool HistoryItem::canPin() const {
return false; return false;
} }
bool HistoryItem::canForward() const { bool HistoryItem::allowsForward() const {
if (id < 0 || isLogEntry()) {
return false;
}
if (auto message = toHistoryMessage()) {
if (auto media = message->getMedia()) {
if (media->type() == MediaTypeCall) {
return false;
}
}
return true;
}
return false; return false;
} }
bool HistoryItem::canEdit(const QDateTime &cur) const { bool HistoryItem::allowsEdit(const QDateTime &now) const {
auto messageToMyself = _history->peer->isSelf();
auto canPinInMegagroup = [&] {
if (auto megagroup = _history->peer->asMegagroup()) {
return megagroup->canPinMessages();
}
return false;
}();
auto messageTooOld = (messageToMyself || canPinInMegagroup)
? false
: (date.secsTo(cur) >= Global::EditTimeLimit());
if (id < 0 || messageTooOld) {
return false;
}
if (auto message = toHistoryMessage()) {
if (message->Has<HistoryMessageVia>() || message->Has<HistoryMessageForwarded>()) {
return false;
}
if (auto media = message->getMedia()) {
if (!media->canEditCaption() && media->type() != MediaTypeWebPage) {
return false;
}
}
if (messageToMyself) {
return true;
}
if (auto channel = _history->peer->asChannel()) {
if (isPost() && channel->canEditMessages()) {
return true;
}
if (out()) {
return !isPost() || channel->canPublish();
}
} else {
return out();
}
}
return false; return false;
} }
@ -443,8 +406,8 @@ bool HistoryItem::canDeleteForEveryone(const QDateTime &cur) const {
if (!toHistoryMessage()) { if (!toHistoryMessage()) {
return false; return false;
} }
if (auto media = getMedia()) { if (const auto media = this->media()) {
if (media->type() == MediaTypeCall) { if (!media->allowsRevoke()) {
return false; return false;
} }
} }
@ -493,8 +456,8 @@ QString HistoryItem::directLink() const {
Assert(channel != nullptr); Assert(channel != nullptr);
auto query = channel->username + '/' + QString::number(id); auto query = channel->username + '/' + QString::number(id);
if (!channel->isMegagroup()) { if (!channel->isMegagroup()) {
if (auto media = getMedia()) { if (const auto media = this->media()) {
if (auto document = media->getDocument()) { if (const auto document = media->document()) {
if (document->isVideoMessage()) { if (document->isVideoMessage()) {
return qsl("https://telesco.pe/") + query; return qsl("https://telesco.pe/") + query;
} }
@ -565,13 +528,6 @@ MsgId HistoryItem::idOriginal() const {
return id; return id;
} }
bool HistoryItem::hasOutLayout() const {
if (history()->peer->isSelf()) {
return !Has<HistoryMessageForwarded>();
}
return out() && !isPost();
}
bool HistoryItem::needCheck() const { bool HistoryItem::needCheck() const {
return out() || (id < 0 && history()->peer->isSelf()); return out() || (id < 0 && history()->peer->isSelf());
} }
@ -658,84 +614,7 @@ void HistoryItem::setUnreadBarFreezed() {
} }
MessageGroupId HistoryItem::groupId() const { MessageGroupId HistoryItem::groupId() const {
if (const auto group = Get<HistoryMessageGroup>()) { return _groupId;
return group->groupId;
}
return MessageGroupId::None;
}
bool HistoryItem::groupIdValidityChanged() {
if (Has<HistoryMessageGroup>()) {
if (_media && _media->canBeGrouped()) {
return false;
}
RemoveComponents(HistoryMessageGroup::Bit());
Auth().data().requestItemViewResize(this);
return true;
}
return false;
}
void HistoryItem::makeGroupMember(not_null<HistoryItem*> leader) {
Expects(leader != this);
const auto group = Get<HistoryMessageGroup>();
Assert(group != nullptr);
if (group->leader == this) {
if (auto single = _media ? _media->takeLastFromGroup() : nullptr) {
_media = std::move(single);
}
_flags |= MTPDmessage_ClientFlag::f_hidden_by_group;
Auth().data().requestItemViewResize(this);
group->leader = leader;
base::take(group->others);
} else if (group->leader != leader) {
group->leader = leader;
}
Ensures(isHiddenByGroup());
Ensures(group->others.empty());
}
void HistoryItem::makeGroupLeader(
std::vector<not_null<HistoryItem*>> &&others) {
const auto group = Get<HistoryMessageGroup>();
Assert(group != nullptr);
const auto leaderChanged = (group->leader != this);
if (leaderChanged) {
group->leader = this;
_flags &= ~MTPDmessage_ClientFlag::f_hidden_by_group;
Auth().data().requestItemViewResize(this);
}
group->others = std::move(others);
if (!_media || !_media->applyGroup(group->others)) {
resetGroupMedia(group->others);
invalidateChatsListEntry();
}
Ensures(!isHiddenByGroup());
}
HistoryMessageGroup *HistoryItem::getFullGroup() {
if (const auto group = Get<HistoryMessageGroup>()) {
if (group->leader == this) {
return group;
}
return group->leader->Get<HistoryMessageGroup>();
}
return nullptr;
}
void HistoryItem::resetGroupMedia(
const std::vector<not_null<HistoryItem*>> &others) {
if (!others.empty()) {
_media = std::make_unique<HistoryGroupedMedia>(this, others);
} else if (_media) {
_media = _media->takeLastFromGroup();
}
Auth().data().requestItemViewResize(this);
} }
int HistoryItem::displayedDateHeight() const { int HistoryItem::displayedDateHeight() const {
@ -758,79 +637,74 @@ bool HistoryItem::isEmpty() const {
void HistoryItem::clipCallback(Media::Clip::Notification notification) { void HistoryItem::clipCallback(Media::Clip::Notification notification) {
using namespace Media::Clip; using namespace Media::Clip;
auto media = getMedia(); auto media = this->media();
if (!media) { if (!media) {
return; return;
} }
auto reader = media->getClipReader(); // #TODO GIFs
if (!reader) { //auto reader = media->getClipReader();
return; //if (!reader) {
} // return;
//}
switch (notification) { //switch (notification) {
case NotificationReinit: { //case NotificationReinit: {
auto stopped = false; // auto stopped = false;
if (reader->autoPausedGif()) { // if (reader->autoPausedGif()) {
auto amVisible = false; // auto amVisible = false;
Auth().data().queryItemVisibility().notify({ this, &amVisible }, true); // Auth().data().queryItemVisibility().notify({ this, &amVisible }, true);
if (!amVisible) { // stop animation if it is not visible // if (!amVisible) { // stop animation if it is not visible
media->stopInline(); // media->stopInline();
if (auto document = media->getDocument()) { // forget data from memory // if (auto document = media->getDocument()) { // forget data from memory
document->forget(); // document->forget();
} // }
stopped = true; // stopped = true;
} // }
} else if (reader->mode() == Media::Clip::Reader::Mode::Video && reader->state() == Media::Clip::State::Finished) { // } else if (reader->mode() == Media::Clip::Reader::Mode::Video && reader->state() == Media::Clip::State::Finished) {
// Stop finished video message. // // Stop finished video message.
media->stopInline(); // media->stopInline();
} // }
if (!stopped) { // if (!stopped) {
Auth().data().requestItemViewResize(this); // Auth().data().requestItemViewResize(this);
Auth().data().markItemLayoutChanged(this); // Auth().data().markItemLayoutChange(this);
Global::RefPendingRepaintItems().insert(this); // Global::RefPendingRepaintItems().insert(this);
} // }
} break; //} break;
case NotificationRepaint: { //case NotificationRepaint: {
if (!reader->currentDisplayed()) { // if (!reader->currentDisplayed()) {
Auth().data().requestItemRepaint(this); // Auth().data().requestItemRepaint(this);
} // }
} break; //} break;
} //}
} }
void HistoryItem::audioTrackUpdated() { void HistoryItem::audioTrackUpdated() {
auto media = getMedia(); auto media = this->media();
if (!media) { if (!media) {
return; return;
} }
auto reader = media->getClipReader(); // #TODO GIFs
if (!reader || reader->mode() != Media::Clip::Reader::Mode::Video) { //auto reader = media->getClipReader();
return; //if (!reader || reader->mode() != Media::Clip::Reader::Mode::Video) {
} // return;
//}
auto audio = reader->audioMsgId(); //auto audio = reader->audioMsgId();
auto current = Media::Player::mixer()->currentState(audio.type()); //auto current = Media::Player::mixer()->currentState(audio.type());
if (current.id != audio || Media::Player::IsStoppedOrStopping(current.state)) { //if (current.id != audio || Media::Player::IsStoppedOrStopping(current.state)) {
media->stopInline(); // media->stopInline();
} else if (Media::Player::IsPaused(current.state) || current.state == Media::Player::State::Pausing) { //} else if (Media::Player::IsPaused(current.state) || current.state == Media::Player::State::Pausing) {
if (!reader->videoPaused()) { // if (!reader->videoPaused()) {
reader->pauseResumeVideo(); // reader->pauseResumeVideo();
} // }
} else { //} else {
if (reader->videoPaused()) { // if (reader->videoPaused()) {
reader->pauseResumeVideo(); // reader->pauseResumeVideo();
} // }
} //}
}
bool HistoryItem::isUnderCursor() const {
if (const auto view = App::hoveredItem()) {
return view->data() == this;
}
return false;
} }
HistoryItem *HistoryItem::previousItem() const { HistoryItem *HistoryItem::previousItem() const {
@ -869,7 +743,7 @@ QString HistoryItem::notificationText() const {
QString HistoryItem::inDialogsText(DrawInDialog way) const { QString HistoryItem::inDialogsText(DrawInDialog way) const {
auto getText = [this]() { auto getText = [this]() {
if (emptyText()) { if (emptyText()) {
return _media ? _media->inDialogsText() : QString(); return _media ? _media->chatsListText() : QString();
} }
return TextUtilities::Clean(_text.originalText()); return TextUtilities::Clean(_text.originalText());
}; };
@ -879,7 +753,7 @@ QString HistoryItem::inDialogsText(DrawInDialog way) const {
return nullptr; return nullptr;
} else if (!_history->peer->isUser() || out()) { } else if (!_history->peer->isUser() || out()) {
return author(); return author();
} else if (_history->peer->isSelf() && !hasOutLayout()) { } else if (_history->peer->isSelf() && !Has<HistoryMessageForwarded>()) {
return senderOriginal(); return senderOriginal();
} }
return nullptr; return nullptr;
@ -914,6 +788,7 @@ void HistoryItem::drawInDialog(
} }
HistoryItem::~HistoryItem() { HistoryItem::~HistoryItem() {
Auth().data().groups().unregisterMessage(this);
App::historyUnregItem(this); App::historyUnregItem(this);
if (id < 0 && !App::quitting()) { if (id < 0 && !App::quitting()) {
Auth().uploader().cancel(fullId()); Auth().uploader().cancel(fullId());
@ -940,3 +815,135 @@ ClickHandlerPtr goToMessageClickHandler(
ClickHandlerPtr goToMessageClickHandler(not_null<HistoryItem*> item) { ClickHandlerPtr goToMessageClickHandler(not_null<HistoryItem*> item) {
return goToMessageClickHandler(item->history()->peer, item->id); return goToMessageClickHandler(item->history()->peer, item->id);
} }
not_null<HistoryItem*> HistoryItem::Create(
not_null<History*> history,
const MTPMessage &message) {
switch (message.type()) {
case mtpc_messageEmpty: {
const auto &data = message.c_messageEmpty();
const auto text = HistoryService::PreparedText {
lang(lng_message_empty)
};
return HistoryService::create(history, data.vid.v, ::date(), text);
} break;
case mtpc_message: {
const auto &data = message.c_message();
enum class MediaCheckResult {
Good,
Unsupported,
Empty,
HasTimeToLive,
};
auto badMedia = MediaCheckResult::Good;
const auto &media = data.vmedia;
if (data.has_media()) switch (media.type()) {
case mtpc_messageMediaEmpty:
case mtpc_messageMediaContact: break;
case mtpc_messageMediaGeo:
switch (media.c_messageMediaGeo().vgeo.type()) {
case mtpc_geoPoint: break;
case mtpc_geoPointEmpty: badMedia = MediaCheckResult::Empty; break;
default: badMedia = MediaCheckResult::Unsupported; break;
}
break;
case mtpc_messageMediaVenue:
switch (media.c_messageMediaVenue().vgeo.type()) {
case mtpc_geoPoint: break;
case mtpc_geoPointEmpty: badMedia = MediaCheckResult::Empty; break;
default: badMedia = MediaCheckResult::Unsupported; break;
}
break;
case mtpc_messageMediaGeoLive:
switch (media.c_messageMediaGeoLive().vgeo.type()) {
case mtpc_geoPoint: break;
case mtpc_geoPointEmpty: badMedia = MediaCheckResult::Empty; break;
default: badMedia = MediaCheckResult::Unsupported; break;
}
break;
case mtpc_messageMediaPhoto: {
auto &photo = media.c_messageMediaPhoto();
if (photo.has_ttl_seconds()) {
badMedia = MediaCheckResult::HasTimeToLive;
} else if (!photo.has_photo()) {
badMedia = MediaCheckResult::Empty;
} else {
switch (photo.vphoto.type()) {
case mtpc_photo: break;
case mtpc_photoEmpty: badMedia = MediaCheckResult::Empty; break;
default: badMedia = MediaCheckResult::Unsupported; break;
}
}
} break;
case mtpc_messageMediaDocument: {
auto &document = media.c_messageMediaDocument();
if (document.has_ttl_seconds()) {
badMedia = MediaCheckResult::HasTimeToLive;
} else if (!document.has_document()) {
badMedia = MediaCheckResult::Empty;
} else {
switch (document.vdocument.type()) {
case mtpc_document: break;
case mtpc_documentEmpty: badMedia = MediaCheckResult::Empty; break;
default: badMedia = MediaCheckResult::Unsupported; break;
}
}
} break;
case mtpc_messageMediaWebPage:
switch (media.c_messageMediaWebPage().vwebpage.type()) {
case mtpc_webPage:
case mtpc_webPageEmpty:
case mtpc_webPagePending: break;
case mtpc_webPageNotModified:
default: badMedia = MediaCheckResult::Unsupported; break;
}
break;
case mtpc_messageMediaGame:
switch (media.c_messageMediaGame().vgame.type()) {
case mtpc_game: break;
default: badMedia = MediaCheckResult::Unsupported; break;
}
break;
case mtpc_messageMediaInvoice:
break;
case mtpc_messageMediaUnsupported:
default: badMedia = MediaCheckResult::Unsupported; break;
}
if (badMedia == MediaCheckResult::Unsupported) {
return CreateUnsupportedMessage(
history,
data.vid.v,
data.vflags.v,
data.vreply_to_msg_id.v,
data.vvia_bot_id.v,
::date(data.vdate),
data.vfrom_id.v);
} else if (badMedia == MediaCheckResult::Empty) {
const auto text = HistoryService::PreparedText {
lang(lng_message_empty)
};
return HistoryService::create(
history,
data.vid.v,
::date(data.vdate),
text,
data.vflags.v,
data.has_from_id() ? data.vfrom_id.v : UserId(0));
} else if (badMedia == MediaCheckResult::HasTimeToLive) {
return HistoryService::create(history, data);
}
return HistoryMessage::create(history, data);
} break;
case mtpc_messageService: {
auto &data = message.c_messageService();
if (data.vaction.type() == mtpc_messageActionPhoneCall) {
return HistoryMessage::create(history, data);
}
return HistoryService::create(history, data);
} break;
}
Unexpected("Type in HistoryItem::Create().");
}

View file

@ -14,8 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_cursor_state.h" #include "history/view/history_view_cursor_state.h"
enum class UnreadMentionType; enum class UnreadMentionType;
struct MessageGroupId;
struct HistoryMessageGroup;
struct HistoryMessageReplyMarkup; struct HistoryMessageReplyMarkup;
class ReplyKeyboard; class ReplyKeyboard;
class HistoryMessage; class HistoryMessage;
@ -42,6 +40,7 @@ struct RippleAnimation;
namespace Data { namespace Data {
struct MessagePosition; struct MessagePosition;
class Media;
} // namespace Data } // namespace Data
namespace Window { namespace Window {
@ -52,21 +51,12 @@ namespace HistoryView {
enum class Context : char; enum class Context : char;
} // namespace HistoryView } // namespace HistoryView
namespace internal { class HistoryItem : public RuntimeComposer<HistoryItem> {
TextSelection unshiftSelection(TextSelection selection, uint16 byLength);
TextSelection shiftSelection(TextSelection selection, uint16 byLength);
inline TextSelection unshiftSelection(TextSelection selection, const Text &byText) {
return ::internal::unshiftSelection(selection, byText.length());
}
inline TextSelection shiftSelection(TextSelection selection, const Text &byText) {
return ::internal::shiftSelection(selection, byText.length());
}
} // namespace internal
class HistoryItem : public RuntimeComposer {
public: public:
static not_null<HistoryItem*> Create(
not_null<History*> history,
const MTPMessage &message);
virtual void dependencyItemRemoved(HistoryItem *dependency) { virtual void dependencyItemRemoved(HistoryItem *dependency) {
} }
virtual bool updateDependencyItem() { virtual bool updateDependencyItem() {
@ -145,19 +135,12 @@ public:
bool isSilent() const { bool isSilent() const {
return _flags & MTPDmessage::Flag::f_silent; return _flags & MTPDmessage::Flag::f_silent;
} }
bool hasOutLayout() const; virtual int viewsCount() const {
virtual int32 viewsCount() const {
return hasViews() ? 1 : -1; return hasViews() ? 1 : -1;
} }
virtual bool needCheck() const; virtual bool needCheck() const;
[[nodiscard]] virtual TextSelection adjustSelection(
TextSelection selection,
TextSelectType type) const {
return selection;
}
virtual bool serviceMsg() const { virtual bool serviceMsg() const {
return false; return false;
} }
@ -173,17 +156,10 @@ public:
virtual void addToUnreadMentions(UnreadMentionType type); virtual void addToUnreadMentions(UnreadMentionType type);
virtual void eraseFromUnreadMentions() { virtual void eraseFromUnreadMentions() {
} }
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const; virtual Storage::SharedMediaTypesMask sharedMediaTypes() const = 0;
void indexAsNewItem(); void indexAsNewItem();
virtual bool hasBubble() const {
return false;
}
virtual TextWithEntities selectedText(TextSelection selection) const {
return { qsl("[-]"), EntitiesInText() };
}
virtual QString notificationHeader() const { virtual QString notificationHeader() const {
return QString(); return QString();
} }
@ -204,29 +180,10 @@ public:
return { QString(), EntitiesInText() }; return { QString(), EntitiesInText() };
} }
virtual void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const {
}
virtual ClickHandlerPtr rightActionLink() const {
return ClickHandlerPtr();
}
virtual bool displayRightAction() const {
return false;
}
virtual void drawRightAction(Painter &p, int left, int top, int outerWidth) const {
}
virtual void setViewsCount(int32 count) { virtual void setViewsCount(int32 count) {
} }
virtual void setRealId(MsgId newId); virtual void setRealId(MsgId newId);
virtual bool displayEditedBadge() const {
return false;
}
virtual QDateTime displayedEditDate() const {
return QDateTime();
}
virtual void refreshEditedBadge() {
}
void drawInDialog( void drawInDialog(
Painter &p, Painter &p,
const QRect &r, const QRect &r,
@ -242,8 +199,8 @@ public:
bool isPinned() const; bool isPinned() const;
bool canPin() const; bool canPin() const;
bool canForward() const; virtual bool allowsForward() const;
bool canEdit(const QDateTime &cur) const; virtual bool allowsEdit(const QDateTime &now) const;
bool canDelete() const; bool canDelete() const;
bool canDeleteForEveryone(const QDateTime &cur) const; bool canDeleteForEveryone(const QDateTime &cur) const;
bool suggestBanReport() const; bool suggestBanReport() const;
@ -261,7 +218,7 @@ public:
} }
Data::MessagePosition position() const; Data::MessagePosition position() const;
HistoryMedia *getMedia() const { Data::Media *media() const {
return _media.get(); return _media.get();
} }
virtual void setText(const TextWithEntities &textWithEntities) { virtual void setText(const TextWithEntities &textWithEntities) {
@ -270,29 +227,6 @@ public:
return false; return false;
} }
virtual int infoWidth() const {
return 0;
}
virtual int timeLeft() const {
return 0;
}
virtual int timeWidth() const {
return 0;
}
virtual bool pointInTime(int right, int bottom, QPoint point, InfoDisplayType type) const {
return false;
}
int skipBlockWidth() const {
return st::msgDateSpace + infoWidth() - st::msgDateDelta.x();
}
int skipBlockHeight() const {
return st::msgDateFont->height - st::msgDateDelta.y();
}
QString skipBlock() const {
return textcmdSkipBlock(skipBlockWidth(), skipBlockHeight());
}
virtual HistoryMessage *toHistoryMessage() { // dynamic_cast optimize virtual HistoryMessage *toHistoryMessage() { // dynamic_cast optimize
return nullptr; return nullptr;
} }
@ -328,25 +262,12 @@ public:
} }
bool isEmpty() const; bool isEmpty() const;
bool isHiddenByGroup() const {
return _flags & MTPDmessage_ClientFlag::f_hidden_by_group;
}
MessageGroupId groupId() const; MessageGroupId groupId() const;
bool groupIdValidityChanged();
void validateGroupId() {
// Just ignore the result.
groupIdValidityChanged();
}
void makeGroupMember(not_null<HistoryItem*> leader);
void makeGroupLeader(std::vector<not_null<HistoryItem*>> &&others);
HistoryMessageGroup *getFullGroup();
void clipCallback(Media::Clip::Notification notification); void clipCallback(Media::Clip::Notification notification);
void audioTrackUpdated(); void audioTrackUpdated();
bool isUnderCursor() const;
HistoryItem *previousItem() const; HistoryItem *previousItem() const;
HistoryItem *nextItem() const; HistoryItem *nextItem() const;
@ -388,27 +309,20 @@ protected:
ReplyKeyboard *inlineReplyKeyboard(); ReplyKeyboard *inlineReplyKeyboard();
void invalidateChatsListEntry(); void invalidateChatsListEntry();
[[nodiscard]] TextSelection skipTextSelection( void setGroupId(MessageGroupId groupId);
TextSelection selection) const {
return internal::unshiftSelection(selection, _text);
}
[[nodiscard]] TextSelection unskipTextSelection(
TextSelection selection) const {
return internal::shiftSelection(selection, _text);
}
Text _text = { int(st::msgMinWidth) }; Text _text = { int(st::msgMinWidth) };
int _textWidth = -1; int _textWidth = -1;
int _textHeight = 0; int _textHeight = 0;
HistoryMediaPtr _media; std::unique_ptr<Data::Media> _media;
private: private:
void resetGroupMedia(const std::vector<not_null<HistoryItem*>> &others);
HistoryView::Element *_mainView = nullptr; HistoryView::Element *_mainView = nullptr;
friend class HistoryView::Element; friend class HistoryView::Element;
MessageGroupId _groupId = MessageGroupId::None;
}; };
// make all the constructors in HistoryItem children protected // make all the constructors in HistoryItem children protected

View file

@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/media_audio.h" #include "media/media_audio.h"
#include "media/player/media_player_instance.h" #include "media/player/media_player_instance.h"
#include "auth_session.h" #include "auth_session.h"
#include "data/data_media_types.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "styles/style_widgets.h" #include "styles/style_widgets.h"
#include "styles/style_history.h" #include "styles/style_history.h"
@ -191,7 +192,7 @@ void HistoryMessageReply::updateName() const {
: App::peerName(replyToMsg->author()); : App::peerName(replyToMsg->author());
replyToName.setText(st::fwdTextStyle, name, Ui::NameTextOptions()); replyToName.setText(st::fwdTextStyle, name, Ui::NameTextOptions());
replyToVersion = replyToMsg->author()->nameVersion; replyToVersion = replyToMsg->author()->nameVersion;
bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false; bool hasPreview = replyToMsg->media() ? replyToMsg->media()->hasReplyPreview() : false;
int32 previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; int32 previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
int32 w = replyToName.maxWidth(); int32 w = replyToName.maxWidth();
if (replyToVia) { if (replyToVia) {
@ -207,7 +208,7 @@ void HistoryMessageReply::updateName() const {
void HistoryMessageReply::resize(int width) const { void HistoryMessageReply::resize(int width) const {
if (replyToVia) { if (replyToVia) {
bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false; bool hasPreview = replyToMsg->media() ? replyToMsg->media()->hasReplyPreview() : false;
int previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; int previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
replyToVia->resize(width - st::msgReplyBarSkip - previewSkip - replyToName.maxWidth() - st::msgServiceFont->spacew); replyToVia->resize(width - st::msgReplyBarSkip - previewSkip - replyToName.maxWidth() - st::msgServiceFont->spacew);
} }
@ -222,7 +223,13 @@ void HistoryMessageReply::itemRemoved(
} }
} }
void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, int y, int w, PaintFlags flags) const { void HistoryMessageReply::paint(
Painter &p,
not_null<const HistoryView::Element*> holder,
int x,
int y,
int w,
PaintFlags flags) const {
bool selected = (flags & PaintFlag::Selected), outbg = holder->hasOutLayout(); bool selected = (flags & PaintFlag::Selected), outbg = holder->hasOutLayout();
style::color bar = st::msgImgReplyBarColor; style::color bar = st::msgImgReplyBarColor;
@ -234,14 +241,14 @@ void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, in
if (w > st::msgReplyBarSkip) { if (w > st::msgReplyBarSkip) {
if (replyToMsg) { if (replyToMsg) {
auto hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false; auto hasPreview = replyToMsg->media() ? replyToMsg->media()->hasReplyPreview() : false;
if (hasPreview && w < st::msgReplyBarSkip + st::msgReplyBarSize.height()) { if (hasPreview && w < st::msgReplyBarSkip + st::msgReplyBarSize.height()) {
hasPreview = false; hasPreview = false;
} }
auto previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; auto previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
if (hasPreview) { if (hasPreview) {
ImagePtr replyPreview = replyToMsg->getMedia()->replyPreview(); const auto replyPreview = replyToMsg->media()->replyPreview();
if (!replyPreview->isNull()) { if (!replyPreview->isNull()) {
auto to = rtlrect(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height(), w + 2 * x); auto to = rtlrect(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height(), w + 2 * x);
auto previewWidth = replyPreview->width() / cIntRetinaFactor(); auto previewWidth = replyPreview->width() / cIntRetinaFactor();
@ -834,11 +841,14 @@ void HistoryMessageDate::paint(Painter &p, int y, int w) const {
HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal() = default; HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal() = default;
HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal(HistoryMessageLogEntryOriginal &&other) : _page(std::move(other._page)) { HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal(
HistoryMessageLogEntryOriginal &&other)
: page(std::move(other.page)) {
} }
HistoryMessageLogEntryOriginal &HistoryMessageLogEntryOriginal::operator=(HistoryMessageLogEntryOriginal &&other) { HistoryMessageLogEntryOriginal &HistoryMessageLogEntryOriginal::operator=(
_page = std::move(other._page); HistoryMessageLogEntryOriginal &&other) {
page = std::move(other.page);
return *this; return *this;
} }

View file

@ -8,38 +8,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "history/history_item.h" #include "history/history_item.h"
#include "base/value_ordering.h"
class HistoryDocument; class HistoryDocument;
class HistoryWebPage; class WebPageData;
struct MessageGroupId { namespace HistoryView {
using Underlying = uint64; class Element;
} // namespace HistoryView
enum Type : Underlying { struct HistoryMessageVia : public RuntimeComponent<HistoryMessageVia, HistoryItem> {
None = 0,
} value;
MessageGroupId(Type value = None) : value(value) {
}
static MessageGroupId FromRaw(Underlying value) {
return static_cast<Type>(value);
}
explicit operator bool() const {
return value != None;
}
Underlying raw() const {
return static_cast<Underlying>(value);
}
friend inline Type value_ordering_helper(MessageGroupId value) {
return value.value;
}
};
struct HistoryMessageVia : public RuntimeComponent<HistoryMessageVia> {
void create(UserId userId); void create(UserId userId);
void resize(int32 availw) const; void resize(int32 availw) const;
@ -50,13 +27,13 @@ struct HistoryMessageVia : public RuntimeComponent<HistoryMessageVia> {
ClickHandlerPtr link; ClickHandlerPtr link;
}; };
struct HistoryMessageViews : public RuntimeComponent<HistoryMessageViews> { struct HistoryMessageViews : public RuntimeComponent<HistoryMessageViews, HistoryItem> {
QString _viewsText; QString _viewsText;
int _views = 0; int _views = 0;
int _viewsWidth = 0; int _viewsWidth = 0;
}; };
struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned> { struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned, HistoryItem> {
void refresh(const QString &date); void refresh(const QString &date);
int maxWidth() const; int maxWidth() const;
@ -64,7 +41,7 @@ struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned> {
Text signature; Text signature;
}; };
struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited> { struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited, HistoryItem> {
void refresh(const QString &date, bool displayed); void refresh(const QString &date, bool displayed);
int maxWidth() const; int maxWidth() const;
@ -72,7 +49,7 @@ struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited> {
Text text; Text text;
}; };
struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded> { struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded, HistoryItem> {
void create(const HistoryMessageVia *via) const; void create(const HistoryMessageVia *via) const;
QDateTime originalDate; QDateTime originalDate;
@ -85,7 +62,7 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded
MsgId savedFromMsgId = 0; MsgId savedFromMsgId = 0;
}; };
struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply> { struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply, HistoryItem> {
HistoryMessageReply() = default; HistoryMessageReply() = default;
HistoryMessageReply(const HistoryMessageReply &other) = delete; HistoryMessageReply(const HistoryMessageReply &other) = delete;
HistoryMessageReply(HistoryMessageReply &&other) = delete; HistoryMessageReply(HistoryMessageReply &&other) = delete;
@ -125,7 +102,7 @@ struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply> {
friend inline constexpr auto is_flag_type(PaintFlag) { return true; }; friend inline constexpr auto is_flag_type(PaintFlag) { return true; };
void paint( void paint(
Painter &p, Painter &p,
const HistoryItem *holder, not_null<const HistoryView::Element*> holder,
int x, int x,
int y, int y,
int w, int w,
@ -171,7 +148,7 @@ struct HistoryMessageMarkupButton {
}; };
struct HistoryMessageReplyMarkup : public RuntimeComponent<HistoryMessageReplyMarkup> { struct HistoryMessageReplyMarkup : public RuntimeComponent<HistoryMessageReplyMarkup, HistoryItem> {
using Button = HistoryMessageMarkupButton; using Button = HistoryMessageMarkupButton;
HistoryMessageReplyMarkup() = default; HistoryMessageReplyMarkup() = default;
@ -348,7 +325,7 @@ private:
// Any HistoryItem can have this Component for // Any HistoryItem can have this Component for
// displaying the day mark above the message. // displaying the day mark above the message.
struct HistoryMessageDate : public RuntimeComponent<HistoryMessageDate> { struct HistoryMessageDate : public RuntimeComponent<HistoryMessageDate, HistoryItem> {
void init(const QDateTime &date); void init(const QDateTime &date);
int height() const; int height() const;
@ -360,7 +337,7 @@ struct HistoryMessageDate : public RuntimeComponent<HistoryMessageDate> {
// Any HistoryItem can have this Component for // Any HistoryItem can have this Component for
// displaying the unread messages bar above the message. // displaying the unread messages bar above the message.
struct HistoryMessageUnreadBar : public RuntimeComponent<HistoryMessageUnreadBar> { struct HistoryMessageUnreadBar : public RuntimeComponent<HistoryMessageUnreadBar, HistoryItem> {
void init(int count); void init(int count);
static int height(); static int height();
@ -381,25 +358,20 @@ struct HistoryMessageUnreadBar : public RuntimeComponent<HistoryMessageUnreadBar
}; };
struct HistoryMessageGroup : public RuntimeComponent<HistoryMessageGroup> {
MessageGroupId groupId = MessageGroupId::None;
HistoryItem *leader = nullptr;
std::vector<not_null<HistoryItem*>> others;
};
// Special type of Component for the channel actions log. // Special type of Component for the channel actions log.
struct HistoryMessageLogEntryOriginal : public RuntimeComponent<HistoryMessageLogEntryOriginal> { struct HistoryMessageLogEntryOriginal
: public RuntimeComponent<HistoryMessageLogEntryOriginal, HistoryItem> {
HistoryMessageLogEntryOriginal(); HistoryMessageLogEntryOriginal();
HistoryMessageLogEntryOriginal(HistoryMessageLogEntryOriginal &&other); HistoryMessageLogEntryOriginal(HistoryMessageLogEntryOriginal &&other);
HistoryMessageLogEntryOriginal &operator=(HistoryMessageLogEntryOriginal &&other); HistoryMessageLogEntryOriginal &operator=(HistoryMessageLogEntryOriginal &&other);
~HistoryMessageLogEntryOriginal(); ~HistoryMessageLogEntryOriginal();
std::unique_ptr<HistoryWebPage> _page; WebPageData *page = nullptr;
}; };
class FileClickHandler; class FileClickHandler;
struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed> { struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed, HistoryDocument> {
std::shared_ptr<FileClickHandler> _linksavel, _linkcancell; std::shared_ptr<FileClickHandler> _linksavel, _linkcancell;
int _thumbw = 0; int _thumbw = 0;
@ -407,13 +379,13 @@ struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed>
mutable QString _link; mutable QString _link;
}; };
struct HistoryDocumentCaptioned : public RuntimeComponent<HistoryDocumentCaptioned> { struct HistoryDocumentCaptioned : public RuntimeComponent<HistoryDocumentCaptioned, HistoryDocument> {
HistoryDocumentCaptioned(); HistoryDocumentCaptioned();
Text _caption; Text _caption;
}; };
struct HistoryDocumentNamed : public RuntimeComponent<HistoryDocumentNamed> { struct HistoryDocumentNamed : public RuntimeComponent<HistoryDocumentNamed, HistoryDocument> {
QString _name; QString _name;
int _namew = 0; int _namew = 0;
}; };
@ -426,7 +398,7 @@ struct HistoryDocumentVoicePlayback {
BasicAnimation _a_progress; BasicAnimation _a_progress;
}; };
class HistoryDocumentVoice : public RuntimeComponent<HistoryDocumentVoice> { class HistoryDocumentVoice : public RuntimeComponent<HistoryDocumentVoice, HistoryDocument> {
// We don't use float64 because components should align to pointer even on 32bit systems. // We don't use float64 because components should align to pointer even on 32bit systems.
static constexpr float64 kFloatToIntMultiplier = 65536.; static constexpr float64 kFloatToIntMultiplier = 65536.;

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_media.h" #include "history/history_media.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/view/history_view_element.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
Storage::SharedMediaTypesMask HistoryMedia::sharedMediaTypes() const { Storage::SharedMediaTypesMask HistoryMedia::sharedMediaTypes() const {
@ -23,11 +24,15 @@ QSize HistoryMedia::countCurrentSize(int newWidth) {
} }
TextSelection HistoryMedia::skipSelection(TextSelection selection) const { TextSelection HistoryMedia::skipSelection(TextSelection selection) const {
return internal::unshiftSelection(selection, fullSelectionLength()); return HistoryView::UnshiftItemSelection(
selection,
fullSelectionLength());
} }
TextSelection HistoryMedia::unskipSelection(TextSelection selection) const { TextSelection HistoryMedia::unskipSelection(TextSelection selection) const {
return internal::shiftSelection(selection, fullSelectionLength()); return HistoryView::ShiftItemSelection(
selection,
fullSelectionLength());
} }
HistoryTextState HistoryMedia::getStateGrouped( HistoryTextState HistoryMedia::getStateGrouped(

View file

@ -52,21 +52,18 @@ enum HistoryMediaType : char {
class HistoryMedia : public HistoryView::Object { class HistoryMedia : public HistoryView::Object {
public: public:
HistoryMedia(not_null<HistoryItem*> parent) : _parent(parent) { using Element = HistoryView::Element;
HistoryMedia(not_null<Element*> parent) : _parent(parent) {
} }
virtual HistoryMediaType type() const = 0; virtual HistoryMediaType type() const = 0;
virtual std::unique_ptr<HistoryMedia> clone(
virtual QString notificationText() const { not_null<Element*> newParent,
return QString(); not_null<Element*> realParent) const {
Unexpected("Attempt to clone a media that can't be grouped.");
} }
// Returns text with link-start and link-end commands for service-color highlighting.
// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
virtual QString inDialogsText() const {
auto result = notificationText();
return result.isEmpty() ? QString() : textcmdLink(1, TextUtilities::Clean(result));
}
virtual TextWithEntities selectedText(TextSelection selection) const = 0; virtual TextWithEntities selectedText(TextSelection selection) const = 0;
bool hasPoint(QPoint point) const { bool hasPoint(QPoint point) const {
@ -85,7 +82,7 @@ public:
virtual bool allowsFastShare() const { virtual bool allowsFastShare() const {
return false; return false;
} }
virtual void refreshParentId(not_null<HistoryItem*> realParent) { virtual void refreshParentId(not_null<Element*> realParent) {
} }
virtual void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const = 0; virtual void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const = 0;
virtual HistoryTextState getState(QPoint point, HistoryStateRequest request) const = 0; virtual HistoryTextState getState(QPoint point, HistoryStateRequest request) const = 0;
@ -109,10 +106,6 @@ public:
TextSelectType type) const { TextSelectType type) const {
return selection; return selection;
} }
[[nodiscard]] virtual bool consumeMessageText(
const TextWithEntities &textWithEntities) {
return false;
}
[[nodiscard]] virtual uint16 fullSelectionLength() const { [[nodiscard]] virtual uint16 fullSelectionLength() const {
return 0; return 0;
} }
@ -132,9 +125,6 @@ public:
virtual bool uploading() const { virtual bool uploading() const {
return false; return false;
} }
virtual std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const = 0;
virtual PhotoData *getPhoto() const { virtual PhotoData *getPhoto() const {
return nullptr; return nullptr;
@ -187,20 +177,10 @@ public:
virtual std::unique_ptr<HistoryMedia> takeLastFromGroup() { virtual std::unique_ptr<HistoryMedia> takeLastFromGroup() {
return nullptr; return nullptr;
} }
virtual bool applyGroup( virtual bool applyGroup(const std::vector<not_null<Element*>> &others) {
const std::vector<not_null<HistoryItem*>> &others) {
return others.empty(); return others.empty();
} }
virtual void updateSentMedia(const MTPMessageMedia &media) {
}
// After sending an inline result we may want to completely recreate
// the media (all media that was generated on client side, for example)
virtual bool needReSetInlineResultMedia(const MTPMessageMedia &media) {
return true;
}
virtual bool animating() const { virtual bool animating() const {
return false; return false;
} }
@ -253,10 +233,6 @@ public:
return false; return false;
} }
virtual bool canEditCaption() const {
return false;
}
// Sometimes click on media in message is overloaded by the message: // Sometimes click on media in message is overloaded by the message:
// (for example it can open a link or a game instead of opening media) // (for example it can open a link or a game instead of opening media)
// But the overloading click handler should be used only when media // But the overloading click handler should be used only when media
@ -271,7 +247,7 @@ public:
protected: protected:
QSize countCurrentSize(int newWidth) override; QSize countCurrentSize(int newWidth) override;
not_null<HistoryItem*> _parent; not_null<Element*> _parent;
MediaInBubbleState _inBubbleState = MediaInBubbleState::None; MediaInBubbleState _inBubbleState = MediaInBubbleState::None;
}; };

View file

@ -18,13 +18,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_history.h" #include "styles/style_history.h"
#include "layout.h" #include "layout.h"
HistoryGroupedMedia::Element::Element(not_null<HistoryItem*> item) HistoryGroupedMedia::Part::Part(not_null<Element*> view)
: item(item) { : view(view) {
} }
HistoryGroupedMedia::HistoryGroupedMedia( HistoryGroupedMedia::HistoryGroupedMedia(
not_null<HistoryItem*> parent, not_null<Element*> parent,
const std::vector<not_null<HistoryItem*>> &others) const std::vector<not_null<Element*>> &others)
: HistoryMedia(parent) : HistoryMedia(parent)
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { , _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
const auto result = applyGroup(others); const auto result = applyGroup(others);
@ -32,23 +32,17 @@ HistoryGroupedMedia::HistoryGroupedMedia(
Ensures(result); Ensures(result);
} }
std::unique_ptr<HistoryMedia> HistoryGroupedMedia::clone(
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const {
return main()->clone(newParent, realParent);
}
QSize HistoryGroupedMedia::countOptimalSize() { QSize HistoryGroupedMedia::countOptimalSize() {
if (_caption.hasSkipBlock()) { if (_caption.hasSkipBlock()) {
_caption.setSkipBlock( _caption.updateSkipBlock(
_parent->skipBlockWidth(), _parent->skipBlockWidth(),
_parent->skipBlockHeight()); _parent->skipBlockHeight());
} }
std::vector<QSize> sizes; std::vector<QSize> sizes;
sizes.reserve(_elements.size()); sizes.reserve(_parts.size());
for (const auto &element : _elements) { for (const auto &part : _parts) {
const auto &media = element.content; const auto &media = part.content;
media->initDimensions(); media->initDimensions();
sizes.push_back(media->sizeForGrouping()); sizes.push_back(media->sizeForGrouping());
} }
@ -58,7 +52,7 @@ QSize HistoryGroupedMedia::countOptimalSize() {
st::historyGroupWidthMax, st::historyGroupWidthMax,
st::historyGroupWidthMin, st::historyGroupWidthMin,
st::historyGroupSkip); st::historyGroupSkip);
Assert(layout.size() == _elements.size()); Assert(layout.size() == _parts.size());
auto maxWidth = 0; auto maxWidth = 0;
auto minHeight = 0; auto minHeight = 0;
@ -66,8 +60,8 @@ QSize HistoryGroupedMedia::countOptimalSize() {
const auto &item = layout[i]; const auto &item = layout[i];
accumulate_max(maxWidth, item.geometry.x() + item.geometry.width()); accumulate_max(maxWidth, item.geometry.x() + item.geometry.width());
accumulate_max(minHeight, item.geometry.y() + item.geometry.height()); accumulate_max(minHeight, item.geometry.y() + item.geometry.height());
_elements[i].initialGeometry = item.geometry; _parts[i].initialGeometry = item.geometry;
_elements[i].sides = item.sides; _parts[i].sides = item.sides;
} }
if (!_caption.isEmpty()) { if (!_caption.isEmpty()) {
@ -93,9 +87,9 @@ QSize HistoryGroupedMedia::countCurrentSize(int newWidth) {
return int(std::round(value * factor)); return int(std::round(value * factor));
}; };
const auto spacing = scale(initialSpacing); const auto spacing = scale(initialSpacing);
for (auto &element : _elements) { for (auto &part : _parts) {
const auto sides = element.sides; const auto sides = part.sides;
const auto initialGeometry = element.initialGeometry; const auto initialGeometry = part.initialGeometry;
const auto needRightSkip = !(sides & RectPart::Right); const auto needRightSkip = !(sides & RectPart::Right);
const auto needBottomSkip = !(sides & RectPart::Bottom); const auto needBottomSkip = !(sides & RectPart::Bottom);
const auto initialLeft = initialGeometry.x(); const auto initialLeft = initialGeometry.x();
@ -114,7 +108,7 @@ QSize HistoryGroupedMedia::countCurrentSize(int newWidth) {
const auto height = scale(initialBottom) const auto height = scale(initialBottom)
- top - top
- (needBottomSkip ? spacing : 0); - (needBottomSkip ? spacing : 0);
element.geometry = QRect(left, top, width, height); part.geometry = QRect(left, top, width, height);
accumulate_max(newHeight, top + height); accumulate_max(newHeight, top + height);
} }
@ -130,10 +124,9 @@ QSize HistoryGroupedMedia::countCurrentSize(int newWidth) {
return { newWidth, newHeight }; return { newWidth, newHeight };
} }
void HistoryGroupedMedia::refreshParentId( void HistoryGroupedMedia::refreshParentId(not_null<Element*> realParent) {
not_null<HistoryItem*> realParent) { for (const auto &part : _parts) {
for (const auto &element : _elements) { part.content->refreshParentId(part.view);
element.content->refreshParentId(element.item);
} }
} }
@ -142,29 +135,29 @@ void HistoryGroupedMedia::draw(
const QRect &clip, const QRect &clip,
TextSelection selection, TextSelection selection,
TimeMs ms) const { TimeMs ms) const {
for (auto i = 0, count = int(_elements.size()); i != count; ++i) { for (auto i = 0, count = int(_parts.size()); i != count; ++i) {
const auto &element = _elements[i]; const auto &part = _parts[i];
const auto elementSelection = (selection == FullSelection) const auto partSelection = (selection == FullSelection)
? FullSelection ? FullSelection
: IsGroupItemSelection(selection, i) : IsGroupItemSelection(selection, i)
? FullSelection ? FullSelection
: TextSelection(); : TextSelection();
auto corners = Ui::GetCornersFromSides(element.sides); auto corners = Ui::GetCornersFromSides(part.sides);
if (!isBubbleTop()) { if (!isBubbleTop()) {
corners &= ~(RectPart::TopLeft | RectPart::TopRight); corners &= ~(RectPart::TopLeft | RectPart::TopRight);
} }
if (!isBubbleBottom() || !_caption.isEmpty()) { if (!isBubbleBottom() || !_caption.isEmpty()) {
corners &= ~(RectPart::BottomLeft | RectPart::BottomRight); corners &= ~(RectPart::BottomLeft | RectPart::BottomRight);
} }
element.content->drawGrouped( part.content->drawGrouped(
p, p,
clip, clip,
elementSelection, partSelection,
ms, ms,
element.geometry, part.geometry,
corners, corners,
&element.cacheKey, &part.cacheKey,
&element.cache); &part.cache);
} }
// date // date
@ -177,7 +170,7 @@ void HistoryGroupedMedia::draw(
- _caption.countHeight(captionw); - _caption.countHeight(captionw);
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg));
_caption.draw(p, st::msgPadding.left(), captiony, captionw, style::al_left, 0, -1, selection); _caption.draw(p, st::msgPadding.left(), captiony, captionw, style::al_left, 0, -1, selection);
} else if (_parent->getMedia() == this) { } else if (_parent->media() == this) {
auto fullRight = width(); auto fullRight = width();
auto fullBottom = height(); auto fullBottom = height();
if (needInfoDisplay()) { if (needInfoDisplay()) {
@ -191,38 +184,38 @@ void HistoryGroupedMedia::draw(
} }
} }
HistoryTextState HistoryGroupedMedia::getElementState( HistoryTextState HistoryGroupedMedia::getPartState(
QPoint point, QPoint point,
HistoryStateRequest request) const { HistoryStateRequest request) const {
for (const auto &element : _elements) { for (const auto &part : _parts) {
if (element.geometry.contains(point)) { if (part.geometry.contains(point)) {
auto result = element.content->getStateGrouped( auto result = part.content->getStateGrouped(
element.geometry, part.geometry,
point, point,
request); request);
result.itemId = element.item->fullId(); result.itemId = part.view->data()->fullId();
return result; return result;
} }
} }
return HistoryTextState(_parent); return HistoryTextState(_parent->data());
} }
HistoryTextState HistoryGroupedMedia::getState( HistoryTextState HistoryGroupedMedia::getState(
QPoint point, QPoint point,
HistoryStateRequest request) const { HistoryStateRequest request) const {
auto result = getElementState(point, request); auto result = getPartState(point, request);
if (!result.link && !_caption.isEmpty()) { if (!result.link && !_caption.isEmpty()) {
const auto captionw = width() - st::msgPadding.left() - st::msgPadding.right(); const auto captionw = width() - st::msgPadding.left() - st::msgPadding.right();
const auto captiony = height() const auto captiony = height()
- (isBubbleBottom() ? st::msgPadding.bottom() : 0) - (isBubbleBottom() ? st::msgPadding.bottom() : 0)
- _caption.countHeight(captionw); - _caption.countHeight(captionw);
if (QRect(st::msgPadding.left(), captiony, captionw, height() - captiony).contains(point)) { if (QRect(st::msgPadding.left(), captiony, captionw, height() - captiony).contains(point)) {
return HistoryTextState(_parent, _caption.getState( return HistoryTextState(_parent->data(), _caption.getState(
point - QPoint(st::msgPadding.left(), captiony), point - QPoint(st::msgPadding.left(), captiony),
captionw, captionw,
request.forText())); request.forText()));
} }
} else if (_parent->getMedia() == this) { } else if (_parent->media() == this) {
auto fullRight = width(); auto fullRight = width();
auto fullBottom = height(); auto fullBottom = height();
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) { if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
@ -241,8 +234,8 @@ HistoryTextState HistoryGroupedMedia::getState(
bool HistoryGroupedMedia::toggleSelectionByHandlerClick( bool HistoryGroupedMedia::toggleSelectionByHandlerClick(
const ClickHandlerPtr &p) const { const ClickHandlerPtr &p) const {
for (const auto &element : _elements) { for (const auto &part : _parts) {
if (element.content->toggleSelectionByHandlerClick(p)) { if (part.content->toggleSelectionByHandlerClick(p)) {
return true; return true;
} }
} }
@ -250,8 +243,8 @@ bool HistoryGroupedMedia::toggleSelectionByHandlerClick(
} }
bool HistoryGroupedMedia::dragItemByHandler(const ClickHandlerPtr &p) const { bool HistoryGroupedMedia::dragItemByHandler(const ClickHandlerPtr &p) const {
for (const auto &element : _elements) { for (const auto &part : _parts) {
if (element.content->dragItemByHandler(p)) { if (part.content->dragItemByHandler(p)) {
return true; return true;
} }
} }
@ -264,14 +257,6 @@ TextSelection HistoryGroupedMedia::adjustSelection(
return _caption.adjustSelection(selection, type); return _caption.adjustSelection(selection, type);
} }
QString HistoryGroupedMedia::notificationText() const {
return WithCaptionNotificationText(lang(lng_in_dlg_photo), _caption);
}
QString HistoryGroupedMedia::inDialogsText() const {
return WithCaptionDialogsText(lang(lng_in_dlg_album), _caption);
}
TextWithEntities HistoryGroupedMedia::selectedText( TextWithEntities HistoryGroupedMedia::selectedText(
TextSelection selection) const { TextSelection selection) const {
if (!IsSubGroupSelection(selection)) { if (!IsSubGroupSelection(selection)) {
@ -279,7 +264,7 @@ TextWithEntities HistoryGroupedMedia::selectedText(
lang(lng_in_dlg_album), lang(lng_in_dlg_album),
_caption, _caption,
selection); selection);
} else if (IsGroupItemSelection(selection, int(_elements.size()) - 1)) { } else if (IsGroupItemSelection(selection, int(_parts.size()) - 1)) {
return main()->selectedText(FullSelection); return main()->selectedText(FullSelection);
} }
return TextWithEntities(); return TextWithEntities();
@ -288,78 +273,77 @@ TextWithEntities HistoryGroupedMedia::selectedText(
void HistoryGroupedMedia::clickHandlerActiveChanged( void HistoryGroupedMedia::clickHandlerActiveChanged(
const ClickHandlerPtr &p, const ClickHandlerPtr &p,
bool active) { bool active) {
for (const auto &element : _elements) { for (const auto &part : _parts) {
element.content->clickHandlerActiveChanged(p, active); part.content->clickHandlerActiveChanged(p, active);
} }
} }
void HistoryGroupedMedia::clickHandlerPressedChanged( void HistoryGroupedMedia::clickHandlerPressedChanged(
const ClickHandlerPtr &p, const ClickHandlerPtr &p,
bool pressed) { bool pressed) {
for (const auto &element : _elements) { for (const auto &part : _parts) {
element.content->clickHandlerPressedChanged(p, pressed); part.content->clickHandlerPressedChanged(p, pressed);
if (pressed && element.content->dragItemByHandler(p)) { if (pressed && part.content->dragItemByHandler(p)) {
// #TODO pressedLinkItem App::pressedLinkItem(part.view);
//App::pressedLinkItem(element.item);
} }
} }
} }
void HistoryGroupedMedia::attachToParent() { void HistoryGroupedMedia::attachToParent() {
for (const auto &element : _elements) { for (const auto &part : _parts) {
element.content->attachToParent(); part.content->attachToParent();
} }
} }
void HistoryGroupedMedia::detachFromParent() { void HistoryGroupedMedia::detachFromParent() {
for (const auto &element : _elements) { for (const auto &part : _parts) {
if (element.content) { if (part.content) {
element.content->detachFromParent(); part.content->detachFromParent();
} }
} }
} }
std::unique_ptr<HistoryMedia> HistoryGroupedMedia::takeLastFromGroup() { std::unique_ptr<HistoryMedia> HistoryGroupedMedia::takeLastFromGroup() {
return std::move(_elements.back().content); return std::move(_parts.back().content);
} }
bool HistoryGroupedMedia::applyGroup( bool HistoryGroupedMedia::applyGroup(
const std::vector<not_null<HistoryItem*>> &others) { const std::vector<not_null<Element*>> &others) {
if (others.empty()) { if (others.empty()) {
return false; return false;
} }
const auto pushElement = [&](not_null<HistoryItem*> item) { const auto pushElement = [&](not_null<Element*> view) {
const auto media = item->getMedia(); const auto media = view->media();
Assert(media != nullptr && media->canBeGrouped()); Assert(media != nullptr && media->canBeGrouped());
_elements.push_back(Element(item)); _parts.push_back(Part(view));
_elements.back().content = item->getMedia()->clone(_parent, item); _parts.back().content = media->clone(_parent, view);
}; };
if (_elements.empty()) { if (_parts.empty()) {
pushElement(_parent); pushElement(_parent);
} else if (validateGroupElements(others)) { } else if (validateGroupParts(others)) {
return true; return true;
} }
// We're updating other elements, so we just need to preserve the main. // We're updating other elements, so we just need to preserve the main.
auto mainElement = std::move(_elements.back()); auto mainPart = std::move(_parts.back());
_elements.erase(_elements.begin(), _elements.end()); _parts.erase(_parts.begin(), _parts.end());
_elements.reserve(others.size() + 1); _parts.reserve(others.size() + 1);
for (const auto item : others) { for (const auto view : others) {
pushElement(item); pushElement(view);
} }
_elements.push_back(std::move(mainElement)); _parts.push_back(std::move(mainPart));
//_parent->setPendingInitDimensions(); // #TODO group view //_parent->setPendingInitDimensions(); // #TODO group view
return true; return true;
} }
bool HistoryGroupedMedia::validateGroupElements( bool HistoryGroupedMedia::validateGroupParts(
const std::vector<not_null<HistoryItem*>> &others) const { const std::vector<not_null<Element*>> &others) const {
if (_elements.size() != others.size() + 1) { if (_parts.size() != others.size() + 1) {
return false; return false;
} }
for (auto i = 0, count = int(others.size()); i != count; ++i) { for (auto i = 0, count = int(others.size()); i != count; ++i) {
if (_elements[i].item != others[i]) { if (_parts[i].view != others[i]) {
return false; return false;
} }
} }
@ -367,9 +351,9 @@ bool HistoryGroupedMedia::validateGroupElements(
} }
not_null<HistoryMedia*> HistoryGroupedMedia::main() const { not_null<HistoryMedia*> HistoryGroupedMedia::main() const {
Expects(!_elements.empty()); Expects(!_parts.empty());
return _elements.back().content.get(); return _parts.back().content.get();
} }
bool HistoryGroupedMedia::hasReplyPreview() const { bool HistoryGroupedMedia::hasReplyPreview() const {
@ -388,15 +372,6 @@ Storage::SharedMediaTypesMask HistoryGroupedMedia::sharedMediaTypes() const {
return main()->sharedMediaTypes(); return main()->sharedMediaTypes();
} }
void HistoryGroupedMedia::updateSentMedia(const MTPMessageMedia &media) {
return main()->updateSentMedia(media);
}
bool HistoryGroupedMedia::needReSetInlineResultMedia(
const MTPMessageMedia &media) {
return main()->needReSetInlineResultMedia(media);
}
PhotoData *HistoryGroupedMedia::getPhoto() const { PhotoData *HistoryGroupedMedia::getPhoto() const {
return main()->getPhoto(); return main()->getPhoto();
} }
@ -407,25 +382,25 @@ DocumentData *HistoryGroupedMedia::getDocument() const {
HistoryMessageEdited *HistoryGroupedMedia::displayedEditBadge() const { HistoryMessageEdited *HistoryGroupedMedia::displayedEditBadge() const {
if (!_caption.isEmpty()) { if (!_caption.isEmpty()) {
return _elements.front().item->Get<HistoryMessageEdited>(); return _parts.front().view->data()->Get<HistoryMessageEdited>();
} }
return nullptr; return nullptr;
} }
void HistoryGroupedMedia::updateNeedBubbleState() { void HistoryGroupedMedia::updateNeedBubbleState() {
const auto getItemCaption = [](const Element &element) { const auto getItemCaption = [](const Part &part) {
if (const auto media = element.item->getMedia()) { if (const auto media = part.view->media()) {
return media->getCaption(); return media->getCaption();
} }
return element.content->getCaption(); return part.content->getCaption();
}; };
const auto captionText = [&] { const auto captionText = [&] {
auto result = getItemCaption(_elements.front()); auto result = getItemCaption(_parts.front());
if (result.text.isEmpty()) { if (result.text.isEmpty()) {
return result; return result;
} }
for (auto i = 1, count = int(_elements.size()); i != count; ++i) { for (auto i = 1, count = int(_parts.size()); i != count; ++i) {
if (!getItemCaption(_elements[i]).text.isEmpty()) { if (!getItemCaption(_parts[i]).text.isEmpty()) {
return TextWithEntities(); return TextWithEntities();
} }
} }
@ -434,7 +409,7 @@ void HistoryGroupedMedia::updateNeedBubbleState() {
_caption.setText( _caption.setText(
st::messageTextStyle, st::messageTextStyle,
captionText.text + _parent->skipBlock(), captionText.text + _parent->skipBlock(),
Ui::ItemTextNoMonoOptions(_parent)); Ui::ItemTextNoMonoOptions(_parent->data()));
_needBubble = computeNeedBubble(); _needBubble = computeNeedBubble();
} }
@ -442,19 +417,15 @@ bool HistoryGroupedMedia::needsBubble() const {
return _needBubble; return _needBubble;
} }
bool HistoryGroupedMedia::canEditCaption() const {
return main()->canEditCaption();
}
bool HistoryGroupedMedia::computeNeedBubble() const { bool HistoryGroupedMedia::computeNeedBubble() const {
if (!_caption.isEmpty()) { if (!_caption.isEmpty()) {
return true; return true;
} }
if (const auto message = _parent->toHistoryMessage()) { if (const auto item = _parent->data()) {
if (message->viaBot() if (item->viaBot()
|| message->Has<HistoryMessageReply>() || item->Has<HistoryMessageReply>()
|| message->displayForwardedFrom() || _parent->displayForwardedFrom()
// || message->displayFromName() // #TODO media views || _parent->displayFromName()
) { ) {
return true; return true;
} }
@ -463,5 +434,5 @@ bool HistoryGroupedMedia::computeNeedBubble() const {
} }
bool HistoryGroupedMedia::needInfoDisplay() const { bool HistoryGroupedMedia::needInfoDisplay() const {
return (_parent->id < 0 || _parent->isUnderCursor()); return (_parent->data()->id < 0 || _parent->isUnderCursor());
} }

View file

@ -14,19 +14,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class HistoryGroupedMedia : public HistoryMedia { class HistoryGroupedMedia : public HistoryMedia {
public: public:
HistoryGroupedMedia( HistoryGroupedMedia(
not_null<HistoryItem*> parent, not_null<Element*> parent,
const std::vector<not_null<HistoryItem*>> &others); const std::vector<not_null<Element*>> &others);
HistoryMediaType type() const override { HistoryMediaType type() const override {
return MediaTypeGrouped; return MediaTypeGrouped;
} }
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override;
void refreshParentId(not_null<HistoryItem*> realParent) override; void refreshParentId(not_null<Element*> realParent) override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
void draw( void draw(
Painter &p, Painter &p,
@ -54,8 +49,6 @@ public:
PhotoData *getPhoto() const override; PhotoData *getPhoto() const override;
DocumentData *getDocument() const override; DocumentData *getDocument() const override;
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
void clickHandlerActiveChanged( void clickHandlerActiveChanged(
@ -69,7 +62,7 @@ public:
void detachFromParent() override; void detachFromParent() override;
std::unique_ptr<HistoryMedia> takeLastFromGroup() override; std::unique_ptr<HistoryMedia> takeLastFromGroup() override;
bool applyGroup( bool applyGroup(
const std::vector<not_null<HistoryItem*>> &others) override; const std::vector<not_null<Element*>> &others) override;
bool hasReplyPreview() const override; bool hasReplyPreview() const override;
ImagePtr replyPreview() override; ImagePtr replyPreview() override;
@ -93,16 +86,15 @@ public:
bool customInfoLayout() const override { bool customInfoLayout() const override {
return _caption.isEmpty(); return _caption.isEmpty();
} }
bool canEditCaption() const override;
bool allowsFastShare() const override { bool allowsFastShare() const override {
return true; return true;
} }
private: private:
struct Element { struct Part {
Element(not_null<HistoryItem*> item); Part(not_null<Element*> view);
not_null<HistoryItem*> item; not_null<Element*> view;
std::unique_ptr<HistoryMedia> content; std::unique_ptr<HistoryMedia> content;
RectParts sides = RectPart::None; RectParts sides = RectPart::None;
@ -119,14 +111,14 @@ private:
bool needInfoDisplay() const; bool needInfoDisplay() const;
bool computeNeedBubble() const; bool computeNeedBubble() const;
not_null<HistoryMedia*> main() const; not_null<HistoryMedia*> main() const;
bool validateGroupElements( bool validateGroupParts(
const std::vector<not_null<HistoryItem*>> &others) const; const std::vector<not_null<Element*>> &others) const;
HistoryTextState getElementState( HistoryTextState getPartState(
QPoint point, QPoint point,
HistoryStateRequest request) const; HistoryStateRequest request) const;
Text _caption; Text _caption;
std::vector<Element> _elements; std::vector<Part> _parts;
bool _needBubble = false; bool _needBubble = false;
}; };

File diff suppressed because it is too large Load diff

View file

@ -21,6 +21,12 @@ struct HistoryMessageVia;
struct HistoryMessageReply; struct HistoryMessageReply;
struct HistoryMessageForwarded; struct HistoryMessageForwarded;
namespace Data {
enum class CallFinishReason : char;
struct Invoice;
struct Call;
} // namespace Data
namespace Media { namespace Media {
namespace Clip { namespace Clip {
class Playback; class Playback;
@ -35,12 +41,6 @@ TextWithEntities WithCaptionSelectedText(
const QString &attachType, const QString &attachType,
const Text &caption, const Text &caption,
TextSelection selection); TextSelection selection);
QString WithCaptionNotificationText(
const QString &attachType,
const Text &caption);
QString WithCaptionDialogsText(
const QString &attachType,
const Text &caption);
class HistoryFileMedia : public HistoryMedia { class HistoryFileMedia : public HistoryMedia {
public: public:
@ -56,7 +56,7 @@ public:
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
void refreshParentId(not_null<HistoryItem*> realParent) override; void refreshParentId(not_null<Element*> realParent) override;
bool allowsFastShare() const override { bool allowsFastShare() const override {
return true; return true;
@ -128,22 +128,22 @@ protected:
class HistoryPhoto : public HistoryFileMedia { class HistoryPhoto : public HistoryFileMedia {
public: public:
HistoryPhoto( HistoryPhoto(
not_null<HistoryItem*> parent, not_null<Element*> parent,
not_null<PhotoData*> photo, not_null<PhotoData*> photo,
const QString &caption); const QString &caption);
HistoryPhoto( HistoryPhoto(
not_null<HistoryItem*> parent, not_null<Element*> parent,
not_null<PeerData*> chat, not_null<PeerData*> chat,
not_null<PhotoData*> photo, not_null<PhotoData*> photo,
int width); int width);
HistoryPhoto( HistoryPhoto(
not_null<HistoryItem*> parent, not_null<Element*> parent,
not_null<PeerData*> chat, not_null<PeerData*> chat,
const MTPDphoto &photo, const MTPDphoto &photo,
int width); int width);
HistoryPhoto( HistoryPhoto(
not_null<HistoryItem*> parent, not_null<Element*> parent,
not_null<HistoryItem*> realParent, not_null<Element*> realParent,
const HistoryPhoto &other); const HistoryPhoto &other);
void init(); void init();
@ -151,8 +151,8 @@ public:
return MediaTypePhoto; return MediaTypePhoto;
} }
std::unique_ptr<HistoryMedia> clone( std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent, not_null<Element*> newParent,
not_null<HistoryItem*> realParent) const override { not_null<Element*> realParent) const override {
return std::make_unique<HistoryPhoto>(newParent, realParent, *this); return std::make_unique<HistoryPhoto>(newParent, realParent, *this);
} }
@ -171,12 +171,8 @@ public:
return !_caption.isEmpty(); return !_caption.isEmpty();
} }
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
PhotoData *getPhoto() const override { PhotoData *getPhoto() const override {
return _data; return _data;
} }
@ -199,12 +195,6 @@ public:
QPoint point, QPoint point,
HistoryStateRequest request) const override; HistoryStateRequest request) const override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
void attachToParent() override;
void detachFromParent() override;
bool hasReplyPreview() const override { bool hasReplyPreview() const override {
return !_data->thumb->isNull(); return !_data->thumb->isNull();
} }
@ -220,9 +210,6 @@ public:
bool skipBubbleTail() const override { bool skipBubbleTail() const override {
return isBubbleBottom() && _caption.isEmpty(); return isBubbleBottom() && _caption.isEmpty();
} }
bool canEditCaption() const override {
return true;
}
bool isReadyForOpen() const override { bool isReadyForOpen() const override {
return _data->loaded(); return _data->loaded();
} }
@ -254,20 +241,20 @@ private:
class HistoryVideo : public HistoryFileMedia { class HistoryVideo : public HistoryFileMedia {
public: public:
HistoryVideo( HistoryVideo(
not_null<HistoryItem*> parent, not_null<Element*> parent,
not_null<DocumentData*> document, not_null<DocumentData*> document,
const QString &caption); const QString &caption);
HistoryVideo( HistoryVideo(
not_null<HistoryItem*> parent, not_null<Element*> parent,
not_null<HistoryItem*> realParent, not_null<Element*> realParent,
const HistoryVideo &other); const HistoryVideo &other);
HistoryMediaType type() const override { HistoryMediaType type() const override {
return MediaTypeVideo; return MediaTypeVideo;
} }
std::unique_ptr<HistoryMedia> clone( std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent, not_null<Element*> newParent,
not_null<HistoryItem*> realParent) const override { not_null<Element*> realParent) const override {
return std::make_unique<HistoryVideo>(newParent, realParent, *this); return std::make_unique<HistoryVideo>(newParent, realParent, *this);
} }
@ -286,12 +273,8 @@ public:
return !_caption.isEmpty(); return !_caption.isEmpty();
} }
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
DocumentData *getDocument() const override { DocumentData *getDocument() const override {
return _data; return _data;
} }
@ -318,12 +301,6 @@ public:
return _data->uploading(); return _data->uploading();
} }
void attachToParent() override;
void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool hasReplyPreview() const override { bool hasReplyPreview() const override {
return !_data->thumb->isNull(); return !_data->thumb->isNull();
} }
@ -339,9 +316,6 @@ public:
bool skipBubbleTail() const override { bool skipBubbleTail() const override {
return isBubbleBottom() && _caption.isEmpty(); return isBubbleBottom() && _caption.isEmpty();
} }
bool canEditCaption() const override {
return true;
}
protected: protected:
float64 dataProgress() const override; float64 dataProgress() const override;
@ -366,15 +340,14 @@ private:
}; };
class HistoryDocument : public HistoryFileMedia, public RuntimeComposer { class HistoryDocument
: public HistoryFileMedia
, public RuntimeComposer<HistoryDocument> {
public: public:
HistoryDocument( HistoryDocument(
not_null<HistoryItem*> parent, not_null<Element*> parent,
not_null<DocumentData*> document, not_null<DocumentData*> document,
const QString &caption); const QString &caption);
HistoryDocument(
not_null<HistoryItem*> parent,
const HistoryDocument &other);
HistoryMediaType type() const override { HistoryMediaType type() const override {
return _data->isVoiceMessage() return _data->isVoiceMessage()
@ -383,13 +356,6 @@ public:
? MediaTypeMusicFile ? MediaTypeMusicFile
: MediaTypeFile); : MediaTypeFile);
} }
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryDocument>(newParent, *this);
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
@ -401,12 +367,8 @@ public:
uint16 fullSelectionLength() const override; uint16 fullSelectionLength() const override;
bool hasTextForCopy() const override; bool hasTextForCopy() const override;
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
bool uploading() const override { bool uploading() const override {
return _data->uploading(); return _data->uploading();
} }
@ -417,12 +379,6 @@ public:
bool playInline(bool autoplay) override; bool playInline(bool autoplay) override;
void attachToParent() override;
void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool hasReplyPreview() const override { bool hasReplyPreview() const override {
return !_data->thumb->isNull(); return !_data->thumb->isNull();
} }
@ -439,15 +395,12 @@ public:
bool hideForwardedFrom() const override { bool hideForwardedFrom() const override {
return _data->isSong(); return _data->isSong();
} }
bool canEditCaption() const override {
return true;
}
void step_voiceProgress(float64 ms, bool timer); void step_voiceProgress(float64 ms, bool timer);
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
void refreshParentId(not_null<HistoryItem*> realParent) override; void refreshParentId(not_null<Element*> realParent) override;
protected: protected:
float64 dataProgress() const override; float64 dataProgress() const override;
@ -476,21 +429,13 @@ private:
class HistoryGif : public HistoryFileMedia { class HistoryGif : public HistoryFileMedia {
public: public:
HistoryGif( HistoryGif(
not_null<HistoryItem*> parent, not_null<Element*> parent,
not_null<DocumentData*> document, not_null<DocumentData*> document,
const QString &caption); const QString &caption);
HistoryGif(not_null<HistoryItem*> parent, const HistoryGif &other);
HistoryMediaType type() const override { HistoryMediaType type() const override {
return MediaTypeGif; return MediaTypeGif;
} }
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryGif>(newParent, *this);
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
@ -507,12 +452,8 @@ public:
return !_caption.isEmpty(); return !_caption.isEmpty();
} }
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
bool uploading() const override { bool uploading() const override {
return _data->uploading(); return _data->uploading();
} }
@ -528,12 +469,6 @@ public:
void stopInline() override; void stopInline() override;
bool isRoundVideoPlaying() const override; bool isRoundVideoPlaying() const override;
void attachToParent() override;
void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool hasReplyPreview() const override { bool hasReplyPreview() const override {
return !_data->thumb->isNull(); return !_data->thumb->isNull();
} }
@ -551,9 +486,6 @@ public:
bool skipBubbleTail() const override { bool skipBubbleTail() const override {
return isBubbleBottom() && _caption.isEmpty(); return isBubbleBottom() && _caption.isEmpty();
} }
bool canEditCaption() const override {
return !_data->isVideoMessage();
}
bool isReadyForOpen() const override { bool isReadyForOpen() const override {
return _data->loaded(); return _data->loaded();
} }
@ -600,19 +532,12 @@ private:
class HistorySticker : public HistoryMedia { class HistorySticker : public HistoryMedia {
public: public:
HistorySticker( HistorySticker(
not_null<HistoryItem*> parent, not_null<Element*> parent,
not_null<DocumentData*> document); not_null<DocumentData*> document);
HistoryMediaType type() const override { HistoryMediaType type() const override {
return MediaTypeSticker; return MediaTypeSticker;
} }
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistorySticker>(newParent, _data);
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
@ -627,19 +552,12 @@ public:
return true; return true;
} }
QString notificationText() const override;
TextWithEntities selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
DocumentData *getDocument() const override { DocumentData *getDocument() const override {
return _data; return _data;
} }
void attachToParent() override;
void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool hasReplyPreview() const override { bool hasReplyPreview() const override {
return !_data->thumb->isNull(); return !_data->thumb->isNull();
} }
@ -674,8 +592,8 @@ private:
class HistoryContact : public HistoryMedia { class HistoryContact : public HistoryMedia {
public: public:
HistoryContact( HistoryContact(
not_null<HistoryItem*> parent, not_null<Element*> parent,
int32 userId, UserId userId,
const QString &first, const QString &first,
const QString &last, const QString &last,
const QString &phone); const QString &phone);
@ -683,18 +601,6 @@ public:
HistoryMediaType type() const override { HistoryMediaType type() const override {
return MediaTypeContact; return MediaTypeContact;
} }
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryContact>(
newParent,
_userId,
_fname,
_lname,
_phone);
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
@ -706,14 +612,11 @@ public:
return true; return true;
} }
QString notificationText() const override;
TextWithEntities selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
void attachToParent() override; void attachToParent() override;
void detachFromParent() override; void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needsBubble() const override { bool needsBubble() const override {
return true; return true;
} }
@ -736,7 +639,7 @@ public:
private: private:
QSize countOptimalSize() override; QSize countOptimalSize() override;
int32 _userId = 0; UserId _userId = 0;
UserData *_contact = nullptr; UserData *_contact = nullptr;
int _phonew = 0; int _phonew = 0;
@ -753,17 +656,12 @@ private:
class HistoryCall : public HistoryMedia { class HistoryCall : public HistoryMedia {
public: public:
HistoryCall( HistoryCall(
not_null<HistoryItem*> parent, not_null<Element*> parent,
const MTPDmessageActionPhoneCall &call); not_null<Data::Call*> call);
HistoryMediaType type() const override { HistoryMediaType type() const override {
return MediaTypeCall; return MediaTypeCall;
} }
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Unexpected("Clone HistoryCall.");
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
@ -775,7 +673,6 @@ public:
return false; return false;
} }
QString notificationText() const override;
TextWithEntities selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
bool needsBubble() const override { bool needsBubble() const override {
@ -785,22 +682,14 @@ public:
return true; return true;
} }
enum class FinishReason { Data::CallFinishReason reason() const;
Missed,
Busy,
Disconnected,
Hangup,
};
FinishReason reason() const {
return _reason;
}
private: private:
using FinishReason = Data::CallFinishReason;
QSize countOptimalSize() override; QSize countOptimalSize() override;
static FinishReason GetReason(const MTPDmessageActionPhoneCall &call); FinishReason _reason;
FinishReason _reason = FinishReason::Missed;
int _duration = 0; int _duration = 0;
QString _text; QString _text;
@ -813,24 +702,14 @@ private:
class HistoryWebPage : public HistoryMedia { class HistoryWebPage : public HistoryMedia {
public: public:
HistoryWebPage( HistoryWebPage(
not_null<HistoryItem*> parent, not_null<Element*> parent,
not_null<WebPageData*> data); not_null<WebPageData*> data);
HistoryWebPage(
not_null<HistoryItem*> parent,
const HistoryWebPage &other);
HistoryMediaType type() const override { HistoryMediaType type() const override {
return MediaTypeWebPage; return MediaTypeWebPage;
} }
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryWebPage>(newParent, *this); void refreshParentId(not_null<Element*> realParent) override;
}
void refreshParentId(not_null<HistoryItem*> realParent) override;
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
@ -930,21 +809,16 @@ private:
class HistoryGame : public HistoryMedia { class HistoryGame : public HistoryMedia {
public: public:
HistoryGame(not_null<HistoryItem*> parent, not_null<GameData*> data); HistoryGame(
HistoryGame(not_null<HistoryItem*> parent, const HistoryGame &other); not_null<Element*> parent,
not_null<GameData*> data,
const TextWithEntities &consumed);
HistoryMediaType type() const override { HistoryMediaType type() const override {
return MediaTypeGame; return MediaTypeGame;
} }
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryGame>(newParent, *this); void refreshParentId(not_null<Element*> realParent) override;
}
void refreshParentId(not_null<HistoryItem*> realParent) override;
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
@ -961,7 +835,6 @@ public:
bool hasTextForCopy() const override { bool hasTextForCopy() const override {
return false; // we do not add _title and _description in FullSelection text copy. return false; // we do not add _title and _description in FullSelection text copy.
} }
bool consumeMessageText(const TextWithEntities &textWithEntities) override;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return _attach && _attach->toggleSelectionByHandlerClick(p); return _attach && _attach->toggleSelectionByHandlerClick(p);
@ -970,7 +843,6 @@ public:
return _attach && _attach->dragItemByHandler(p); return _attach && _attach->dragItemByHandler(p);
} }
QString notificationText() const override;
TextWithEntities selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
@ -1004,9 +876,6 @@ public:
return _data; return _data;
} }
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool needsBubble() const override { bool needsBubble() const override {
return true; return true;
} }
@ -1045,24 +914,14 @@ private:
class HistoryInvoice : public HistoryMedia { class HistoryInvoice : public HistoryMedia {
public: public:
HistoryInvoice( HistoryInvoice(
not_null<HistoryItem*> parent, not_null<Element*> parent,
const MTPDmessageMediaInvoice &data); not_null<Data::Invoice*> invoice);
HistoryInvoice(
not_null<HistoryItem*> parent,
const HistoryInvoice &other);
HistoryMediaType type() const override { HistoryMediaType type() const override {
return MediaTypeInvoice; return MediaTypeInvoice;
} }
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryInvoice>(newParent, *this); void refreshParentId(not_null<Element*> realParent) override;
}
void refreshParentId(not_null<HistoryItem*> realParent) override;
MsgId getReceiptMsgId() const { MsgId getReceiptMsgId() const {
return _receiptMsgId; return _receiptMsgId;
@ -1092,7 +951,6 @@ public:
return _attach && _attach->dragItemByHandler(p); return _attach && _attach->dragItemByHandler(p);
} }
QString notificationText() const override;
TextWithEntities selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
@ -1123,7 +981,7 @@ private:
QSize countOptimalSize() override; QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override; QSize countCurrentSize(int newWidth) override;
void fillFromData(const MTPDmessageMediaInvoice &data); void fillFromData(not_null<Data::Invoice*> invoice);
TextSelection toDescriptionSelection(TextSelection selection) const; TextSelection toDescriptionSelection(TextSelection selection) const;
TextSelection fromDescriptionSelection(TextSelection selection) const; TextSelection fromDescriptionSelection(TextSelection selection) const;
@ -1148,24 +1006,14 @@ struct LocationData;
class HistoryLocation : public HistoryMedia { class HistoryLocation : public HistoryMedia {
public: public:
HistoryLocation( HistoryLocation(
not_null<HistoryItem*> parent, not_null<Element*> parent,
const LocationCoords &coords, not_null<LocationData*> location,
const QString &title = QString(), const QString &title = QString(),
const QString &description = QString()); const QString &description = QString());
HistoryLocation(
not_null<HistoryItem*> parent,
const HistoryLocation &other);
HistoryMediaType type() const override { HistoryMediaType type() const override {
return MediaTypeLocation; return MediaTypeLocation;
} }
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryLocation>(newParent, *this);
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
@ -1187,8 +1035,6 @@ public:
return p == _link; return p == _link;
} }
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
bool needsBubble() const override; bool needsBubble() const override;

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,7 @@ QString GetErrorTextForForward(
not_null<PeerData*> peer, not_null<PeerData*> peer,
const HistoryItemsList &items); const HistoryItemsList &items);
void FastShareMessage(not_null<HistoryItem*> item); void FastShareMessage(not_null<HistoryItem*> item);
QString FormatViewsCount(int views);
class HistoryMessage class HistoryMessage
: public HistoryItem : public HistoryItem
@ -142,37 +143,22 @@ public:
markup); markup);
} }
void initTime();
void initMedia(const MTPMessageMedia *media); void initMedia(const MTPMessageMedia *media);
void initMediaFromDocument(DocumentData *doc, const QString &caption);
int32 plainMaxWidth() const; int32 plainMaxWidth() const;
bool drawBubble() const; bool allowsForward() const override;
bool hasBubble() const override { bool allowsEdit(const QDateTime &now) const override;
return drawBubble();
}
bool hasFastReply() const;
bool displayFastReply() const;
bool displayForwardedFrom() const;
bool uploading() const; bool uploading() const;
bool displayRightAction() const override;
void applyGroupAdminChanges( void applyGroupAdminChanges(
const base::flat_map<UserId, bool> &changes) override; const base::flat_map<UserId, bool> &changes) override;
void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const override;
void drawRightAction(Painter &p, int left, int top, int outerWidth) const override;
void setViewsCount(int32 count) override; void setViewsCount(int32 count) override;
void setRealId(MsgId newId) override; void setRealId(MsgId newId) override;
ClickHandlerPtr rightActionLink() const override;
void dependencyItemRemoved(HistoryItem *dependency) override; void dependencyItemRemoved(HistoryItem *dependency) override;
bool pointInTime(int right, int bottom, QPoint point, InfoDisplayType type) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
QString notificationHeader() const override; QString notificationHeader() const override;
void applyEdition(const MTPDmessage &message) override; void applyEdition(const MTPDmessage &message) override;
@ -186,20 +172,10 @@ public:
void eraseFromUnreadMentions() override; void eraseFromUnreadMentions() override;
Storage::SharedMediaTypesMask sharedMediaTypes() const override; Storage::SharedMediaTypesMask sharedMediaTypes() const override;
TextWithEntities selectedText(TextSelection selection) const override;
void setText(const TextWithEntities &textWithEntities) override; void setText(const TextWithEntities &textWithEntities) override;
TextWithEntities originalText() const override; TextWithEntities originalText() const override;
bool textHasLinks() const override; bool textHasLinks() const override;
bool displayEditedBadge() const override;
QDateTime displayedEditDate() const override;
int infoWidth() const override;
int timeLeft() const override;
int timeWidth() const override {
return _timeWidth;
}
int viewsCount() const override; int viewsCount() const override;
not_null<PeerData*> displayFrom() const; not_null<PeerData*> displayFrom() const;
bool updateDependencyItem() override; bool updateDependencyItem() override;
@ -220,9 +196,6 @@ public:
~HistoryMessage(); ~HistoryMessage();
protected:
void refreshEditedBadge() override;
private: private:
HistoryMessage( HistoryMessage(
not_null<History*> history, not_null<History*> history,
@ -295,16 +268,9 @@ private:
void replaceBuyWithReceiptInMarkup(); void replaceBuyWithReceiptInMarkup();
void applyEditionToEmpty(); void applyEditionToEmpty();
QDateTime displayedEditDate(bool hasViaBotOrInlineMarkup) const;
const HistoryMessageEdited *displayedEditBadge() const;
HistoryMessageEdited *displayedEditBadge();
void setMedia(const MTPMessageMedia *media);
void setReplyMarkup(const MTPReplyMarkup *markup); void setReplyMarkup(const MTPReplyMarkup *markup);
bool displayFastShare() const;
bool displayGoToOriginal() const;
struct CreateConfig; struct CreateConfig;
void createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, UserId viaBotId, const QString &postAuthor, const MTPReplyMarkup &markup); void createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, UserId viaBotId, const QString &postAuthor, const MTPReplyMarkup &markup);
void createComponents(const CreateConfig &config); void createComponents(const CreateConfig &config);
@ -315,8 +281,6 @@ private:
QString _timeText; QString _timeText;
int _timeWidth = 0; int _timeWidth = 0;
mutable ClickHandlerPtr _rightActionLink;
mutable ClickHandlerPtr _fastReplyLink;
mutable int32 _fromNameVersion = 0; mutable int32 _fromNameVersion = 0;
friend class HistoryView::Element; friend class HistoryView::Element;

View file

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_service_message.h" #include "history/view/history_view_service_message.h"
#include "data/data_feed.h" #include "data/data_feed.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_types.h"
#include "window/notifications_manager.h" #include "window/notifications_manager.h"
#include "window/window_controller.h" #include "window/window_controller.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
@ -204,7 +205,10 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
case mtpc_messageActionChatEditPhoto: { case mtpc_messageActionChatEditPhoto: {
auto &photo = action.c_messageActionChatEditPhoto().vphoto; auto &photo = action.c_messageActionChatEditPhoto().vphoto;
if (photo.type() == mtpc_photo) { if (photo.type() == mtpc_photo) {
_media = std::make_unique<HistoryPhoto>(this, history()->peer, photo.c_photo(), st::msgServicePhotoWidth); _media = std::make_unique<Data::MediaPhoto>(
this,
history()->peer,
App::feedPhoto(photo.c_photo()));
} }
} break; } break;
@ -268,42 +272,12 @@ HistoryService::PreparedText HistoryService::preparePinnedText() {
auto result = PreparedText {}; auto result = PreparedText {};
auto pinned = Get<HistoryServicePinned>(); auto pinned = Get<HistoryServicePinned>();
if (pinned && pinned->msg) { if (pinned && pinned->msg) {
auto mediaText = ([pinned]() -> QString { const auto mediaText = [&] {
auto media = pinned->msg->getMedia(); if (const auto media = pinned->msg->media()) {
switch (media ? media->type() : MediaTypeCount) { return media->pinnedTextSubstring();
case MediaTypePhoto: return lang(lng_action_pinned_media_photo);
case MediaTypeVideo: return lang(lng_action_pinned_media_video);
case MediaTypeGrouped: return lang(media->getPhoto()
? lng_action_pinned_media_photo
: lng_action_pinned_media_video);
case MediaTypeContact: return lang(lng_action_pinned_media_contact);
case MediaTypeFile: return lang(lng_action_pinned_media_file);
case MediaTypeGif: {
if (auto document = media->getDocument()) {
if (document->isVideoMessage()) {
return lang(lng_action_pinned_media_video_message);
}
}
return lang(lng_action_pinned_media_gif);
} break;
case MediaTypeSticker: {
auto emoji = static_cast<HistorySticker*>(media)->emoji();
if (emoji.isEmpty()) {
return lang(lng_action_pinned_media_sticker);
}
return lng_action_pinned_media_emoji_sticker(lt_emoji, emoji);
} break;
case MediaTypeLocation: return lang(lng_action_pinned_media_location);
case MediaTypeMusicFile: return lang(lng_action_pinned_media_audio);
case MediaTypeVoiceFile: return lang(lng_action_pinned_media_voice);
case MediaTypeGame: {
auto title = static_cast<HistoryGame*>(media)->game()->title;
return lng_action_pinned_media_game(lt_game, title);
} break;
} }
return QString(); return QString();
})(); }();
result.links.push_back(fromLink()); result.links.push_back(fromLink());
result.links.push_back(pinned->lnk); result.links.push_back(pinned->lnk);
if (mediaText.isEmpty()) { if (mediaText.isEmpty()) {
@ -344,8 +318,8 @@ HistoryService::PreparedText HistoryService::prepareGameScoreText() {
auto computeGameTitle = [gamescore, &result]() -> QString { auto computeGameTitle = [gamescore, &result]() -> QString {
if (gamescore && gamescore->msg) { if (gamescore && gamescore->msg) {
if (const auto media = gamescore->msg->getMedia()) { if (const auto media = gamescore->msg->media()) {
if (media->type() == MediaTypeGame) { if (const auto game = media->game()) {
const auto row = 0; const auto row = 0;
const auto column = 0; const auto column = 0;
result.links.push_back( result.links.push_back(
@ -353,7 +327,7 @@ HistoryService::PreparedText HistoryService::prepareGameScoreText() {
row, row,
column, column,
gamescore->msg->fullId())); gamescore->msg->fullId()));
auto titleText = static_cast<HistoryGame*>(media)->game()->title; auto titleText = game->title;
return textcmdLink(result.links.size(), titleText); return textcmdLink(result.links.size(), titleText);
} }
} }
@ -404,11 +378,11 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() {
auto result = PreparedText {}; auto result = PreparedText {};
auto payment = Get<HistoryServicePayment>(); auto payment = Get<HistoryServicePayment>();
auto invoiceTitle = ([payment]() -> QString { auto invoiceTitle = [&] {
if (payment && payment->msg) { if (payment && payment->msg) {
if (auto media = payment->msg->getMedia()) { if (const auto media = payment->msg->media()) {
if (media->type() == MediaTypeInvoice) { if (const auto invoice = media->invoice()) {
return static_cast<HistoryInvoice*>(media)->getTitle(); return invoice->title;
} }
} }
return lang(lng_deleted_message); return lang(lng_deleted_message);
@ -416,7 +390,7 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() {
return lang(lng_contacts_loading); return lang(lng_contacts_loading);
} }
return QString(); return QString();
})(); }();
if (invoiceTitle.isEmpty()) { if (invoiceTitle.isEmpty()) {
result.text = lng_action_payment_done(lt_amount, payment->amount, lt_user, history()->peer->name); result.text = lng_action_payment_done(lt_amount, payment->amount, lt_user, history()->peer->name);
@ -431,16 +405,33 @@ HistoryService::HistoryService(not_null<History*> history, const MTPDmessage &me
createFromMtp(message); createFromMtp(message);
} }
HistoryService::HistoryService(not_null<History*> history, const MTPDmessageService &message) : HistoryService::HistoryService(
HistoryItem(history, message.vid.v, mtpCastFlags(message.vflags.v), ::date(message.vdate), message.has_from_id() ? message.vfrom_id.v : 0) { not_null<History*> history,
const MTPDmessageService &message)
: HistoryItem(
history,
message.vid.v,
mtpCastFlags(message.vflags.v),
::date(message.vdate),
message.has_from_id() ? message.vfrom_id.v : 0) {
createFromMtp(message); createFromMtp(message);
} }
HistoryService::HistoryService(not_null<History*> history, MsgId msgId, QDateTime date, const PreparedText &message, MTPDmessage::Flags flags, int32 from, PhotoData *photo) : HistoryService::HistoryService(
HistoryItem(history, msgId, flags, date, from) { not_null<History*> history,
MsgId msgId,
QDateTime date,
const PreparedText &message,
MTPDmessage::Flags flags,
UserId from,
PhotoData *photo)
: HistoryItem(history, msgId, flags, date, from) {
setServiceText(message); setServiceText(message);
if (photo) { if (photo) {
_media = std::make_unique<HistoryPhoto>(this, history->peer, photo, st::msgServicePhotoWidth); _media = std::make_unique<Data::MediaPhoto>(
this,
history->peer,
photo);
} }
} }
@ -451,10 +442,6 @@ bool HistoryService::updateDependencyItem() {
return HistoryItem::updateDependencyItem(); return HistoryItem::updateDependencyItem();
} }
TextWithEntities HistoryService::selectedText(TextSelection selection) const {
return _text.originalTextWithEntities((selection == FullSelection) ? AllTextSelection : selection);
}
QString HistoryService::inDialogsText(DrawInDialog way) const { QString HistoryService::inDialogsText(DrawInDialog way) const {
return textcmdLink(1, TextUtilities::Clean(notificationText())); return textcmdLink(1, TextUtilities::Clean(notificationText()));
} }
@ -599,16 +586,14 @@ void HistoryService::applyEdition(const MTPDmessageService &message) {
void HistoryService::removeMedia() { void HistoryService::removeMedia() {
if (!_media) return; if (!_media) return;
bool mediaWasDisplayed = _media->isDisplayed();
_media.reset(); _media.reset();
if (mediaWasDisplayed) {
_textWidth = -1; _textWidth = -1;
_textHeight = 0; _textHeight = 0;
} Auth().data().requestItemViewResize(this);
} }
Storage::SharedMediaTypesMask HistoryService::sharedMediaTypes() const { Storage::SharedMediaTypesMask HistoryService::sharedMediaTypes() const {
if (auto media = getMedia()) { if (auto media = this->media()) {
return media->sharedMediaTypes(); return media->sharedMediaTypes();
} }
return {}; return {};

View file

@ -20,24 +20,24 @@ struct HistoryServiceDependentData {
}; };
struct HistoryServicePinned struct HistoryServicePinned
: public RuntimeComponent<HistoryServicePinned> : public RuntimeComponent<HistoryServicePinned, HistoryItem>
, public HistoryServiceDependentData { , public HistoryServiceDependentData {
}; };
struct HistoryServiceGameScore struct HistoryServiceGameScore
: public RuntimeComponent<HistoryServiceGameScore> : public RuntimeComponent<HistoryServiceGameScore, HistoryItem>
, public HistoryServiceDependentData { , public HistoryServiceDependentData {
int score = 0; int score = 0;
}; };
struct HistoryServicePayment struct HistoryServicePayment
: public RuntimeComponent<HistoryServicePayment> : public RuntimeComponent<HistoryServicePayment, HistoryItem>
, public HistoryServiceDependentData { , public HistoryServiceDependentData {
QString amount; QString amount;
}; };
struct HistoryServiceSelfDestruct struct HistoryServiceSelfDestruct
: public RuntimeComponent<HistoryServiceSelfDestruct> { : public RuntimeComponent<HistoryServiceSelfDestruct, HistoryItem> {
enum class Type { enum class Type {
Photo, Photo,
Video, Video,
@ -82,12 +82,6 @@ public:
return true; return true;
} }
[[nodiscard]] TextSelection adjustSelection(
TextSelection selection,
TextSelectType type) const override {
return _text.adjustSelection(selection, type);
}
void applyEdition(const MTPDmessageService &message) override; void applyEdition(const MTPDmessageService &message) override;
TimeMs getSelfDestructIn(TimeMs now) override; TimeMs getSelfDestructIn(TimeMs now) override;
@ -99,7 +93,6 @@ public:
bool serviceMsg() const override { bool serviceMsg() const override {
return true; return true;
} }
TextWithEntities selectedText(TextSelection selection) const override;
QString inDialogsText(DrawInDialog way) const override; QString inDialogsText(DrawInDialog way) const override;
QString inReplyText() const override; QString inReplyText() const override;

View file

@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "inline_bots/inline_bot_result.h" #include "inline_bots/inline_bot_result.h"
#include "data/data_drafts.h" #include "data/data_drafts.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_types.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_message.h" #include "history/history_message.h"
@ -648,10 +649,12 @@ HistoryWidget::HistoryWidget(QWidget *parent, not_null<Window::Controller*> cont
Auth().data().itemLayoutChanged( Auth().data().itemLayoutChanged(
) | rpl::start_with_next([this](auto item) { ) | rpl::start_with_next([this](auto item) {
if (_peer && _list) { if (_peer && _list) {
if (item->isUnderCursor()) { if (const auto view = item->mainView()) {
if (view->isUnderCursor()) {
_list->onUpdateSelected(); _list->onUpdateSelected();
} }
} }
}
}, lifetime()); }, lifetime());
_topBar->membersShowAreaActive( _topBar->membersShowAreaActive(
) | rpl::start_with_next([this](bool active) { ) | rpl::start_with_next([this](bool active) {
@ -714,7 +717,10 @@ void HistoryWidget::animatedScrollToItem(MsgId msgId) {
return; return;
} }
auto scrollTo = snap(itemTopForHighlight(to), 0, _scroll->scrollTopMax()); auto scrollTo = snap(
itemTopForHighlight(to->mainView()),
0,
_scroll->scrollTopMax());
animatedScrollToY(scrollTo, to); animatedScrollToY(scrollTo, to);
} }
@ -769,9 +775,10 @@ void HistoryWidget::scrollToAnimationCallback(FullMsgId attachToId) {
} }
} }
void HistoryWidget::enqueueMessageHighlight(not_null<HistoryItem*> item) { void HistoryWidget::enqueueMessageHighlight(
if (const auto group = item->getFullGroup()) { not_null<HistoryView::Element*> view) {
item = group->leader; if (const auto group = view->getFullGroup()) {
view = group->leader;
} }
auto enqueueMessageId = [this](MsgId universalId) { auto enqueueMessageId = [this](MsgId universalId) {
if (_highlightQueue.empty() && !_highlightTimer.isActive()) { if (_highlightQueue.empty() && !_highlightTimer.isActive()) {
@ -782,6 +789,7 @@ void HistoryWidget::enqueueMessageHighlight(not_null<HistoryItem*> item) {
checkNextHighlight(); checkNextHighlight();
} }
}; };
const auto item = view->data();
if (item->history() == _history) { if (item->history() == _history) {
enqueueMessageId(item->id); enqueueMessageId(item->id);
} else if (item->history() == _migrated) { } else if (item->history() == _migrated) {
@ -874,12 +882,10 @@ void HistoryWidget::clearHighlightMessages() {
stopMessageHighlight(); stopMessageHighlight();
} }
int HistoryWidget::itemTopForHighlight(not_null<HistoryItem*> item) const { int HistoryWidget::itemTopForHighlight(
const auto view = item->mainView(); not_null<HistoryView::Element*> view) const {
Assert(view != nullptr); if (const auto group = view->getFullGroup()) {
view = group->leader;
if (const auto group = item->getFullGroup()) {
item = group->leader;
} }
auto itemTop = _list->itemTop(view); auto itemTop = _list->itemTop(view);
Assert(itemTop >= 0); Assert(itemTop >= 0);
@ -4354,8 +4360,8 @@ void HistoryWidget::onThumbDocumentUploaded(
void HistoryWidget::onPhotoProgress(const FullMsgId &newId) { void HistoryWidget::onPhotoProgress(const FullMsgId &newId) {
if (const auto item = App::histItemById(newId)) { if (const auto item = App::histItemById(newId)) {
const auto photo = item->getMedia() const auto photo = item->media()
? item->getMedia()->getPhoto() ? item->media()->photo()
: nullptr; : nullptr;
updateSendAction(item->history(), SendAction::Type::UploadPhoto, 0); updateSendAction(item->history(), SendAction::Type::UploadPhoto, 0);
Auth().data().requestItemRepaint(item); Auth().data().requestItemRepaint(item);
@ -4364,8 +4370,8 @@ void HistoryWidget::onPhotoProgress(const FullMsgId &newId) {
void HistoryWidget::onDocumentProgress(const FullMsgId &newId) { void HistoryWidget::onDocumentProgress(const FullMsgId &newId) {
if (const auto item = App::histItemById(newId)) { if (const auto item = App::histItemById(newId)) {
const auto media = item->getMedia(); const auto media = item->media();
const auto document = media ? media->getDocument() : nullptr; const auto document = media ? media->document() : nullptr;
const auto sendAction = (document && document->isVoiceMessage()) const auto sendAction = (document && document->isVoiceMessage())
? SendAction::Type::UploadVoice ? SendAction::Type::UploadVoice
: SendAction::Type::UploadFile; : SendAction::Type::UploadFile;
@ -4389,8 +4395,8 @@ void HistoryWidget::onPhotoFailed(const FullMsgId &newId) {
void HistoryWidget::onDocumentFailed(const FullMsgId &newId) { void HistoryWidget::onDocumentFailed(const FullMsgId &newId) {
if (const auto item = App::histItemById(newId)) { if (const auto item = App::histItemById(newId)) {
const auto media = item->getMedia(); const auto media = item->media();
const auto document = media ? media->getDocument() : nullptr; const auto document = media ? media->document() : nullptr;
const auto sendAction = (document && document->isVoiceMessage()) const auto sendAction = (document && document->isVoiceMessage())
? SendAction::Type::UploadVoice ? SendAction::Type::UploadVoice
: SendAction::Type::UploadFile; : SendAction::Type::UploadFile;
@ -4666,8 +4672,11 @@ int HistoryWidget::countInitialScrollTop() {
setMsgId(0); setMsgId(0);
return countInitialScrollTop(); return countInitialScrollTop();
} else { } else {
result = itemTopForHighlight(item); const auto view = item->mainView();
enqueueMessageHighlight(item); Assert(view != nullptr);
result = itemTopForHighlight(view);
enqueueMessageHighlight(view);
} }
} else if (_history->unreadBar || (_migrated && _migrated->unreadBar)) { } else if (_history->unreadBar || (_migrated && _migrated->unreadBar)) {
result = unreadBarTop(); result = unreadBarTop();
@ -5076,7 +5085,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
} }
} else if (e->key() == Qt::Key_Up) { } else if (e->key() == Qt::Key_Up) {
if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) {
if (_history && _history->lastSentMsg && _history->lastSentMsg->canEdit(::date(unixtime()))) { if (_history && _history->lastSentMsg && _history->lastSentMsg->allowsEdit(::date(unixtime()))) {
if (_field->isEmpty() && !_editMsgId && !_replyToId && _history->lastSentMsg) { if (_field->isEmpty() && !_editMsgId && !_replyToId && _history->lastSentMsg) {
editMessage(_history->lastSentMsg); editMessage(_history->lastSentMsg);
return; return;
@ -5598,8 +5607,8 @@ void HistoryWidget::editMessage(FullMsgId itemId) {
} }
void HistoryWidget::editMessage(not_null<HistoryItem*> item) { void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
if (const auto media = item->getMedia()) { if (const auto media = item->media()) {
if (media->canEditCaption()) { if (media->allowsEditCaption()) {
Ui::show(Box<EditCaptionBox>(media, item->fullId())); Ui::show(Box<EditCaptionBox>(media, item->fullId()));
return; return;
} }
@ -5635,9 +5644,9 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
applyDraft(false); applyDraft(false);
_previewData = nullptr; _previewData = nullptr;
if (const auto media = item->getMedia()) { if (const auto media = item->media()) {
if (media->type() == MediaTypeWebPage) { if (const auto page = media->webpage()) {
_previewData = static_cast<HistoryWebPage*>(media)->webpage(); _previewData = page;
updatePreview(); updatePreview();
} }
} }
@ -6335,8 +6344,8 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
(_editMsgId ? st::historyEditIcon : st::historyReplyIcon).paint(p, st::historyReplyIconPosition + QPoint(0, backy), width()); (_editMsgId ? st::historyEditIcon : st::historyReplyIcon).paint(p, st::historyReplyIconPosition + QPoint(0, backy), width());
if (!drawWebPagePreview) { if (!drawWebPagePreview) {
if (drawMsgText) { if (drawMsgText) {
if (drawMsgText->getMedia() && drawMsgText->getMedia()->hasReplyPreview()) { if (drawMsgText->media() && drawMsgText->media()->hasReplyPreview()) {
auto replyPreview = drawMsgText->getMedia()->replyPreview(); auto replyPreview = drawMsgText->media()->replyPreview();
if (!replyPreview->isNull()) { if (!replyPreview->isNull()) {
auto to = QRect(replyLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); auto to = QRect(replyLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height(), ImageRoundRadius::Small)); p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height(), ImageRoundRadius::Small));
@ -6362,7 +6371,7 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
st::historyForwardIcon.paint(p, st::historyReplyIconPosition + QPoint(0, backy), width()); st::historyForwardIcon.paint(p, st::historyReplyIconPosition + QPoint(0, backy), width());
if (!drawWebPagePreview) { if (!drawWebPagePreview) {
const auto firstItem = _toForward.front(); const auto firstItem = _toForward.front();
const auto firstMedia = firstItem->getMedia(); const auto firstMedia = firstItem->media();
const auto serviceColor = (_toForward.size() > 1) const auto serviceColor = (_toForward.size() > 1)
|| (firstMedia != nullptr) || (firstMedia != nullptr)
|| firstItem->serviceMsg(); || firstItem->serviceMsg();
@ -6498,8 +6507,8 @@ void HistoryWidget::drawPinnedBar(Painter &p) {
int32 left = st::msgReplyBarSkip + st::msgReplyBarSkip; int32 left = st::msgReplyBarSkip + st::msgReplyBarSkip;
if (_pinnedBar->msg) { if (_pinnedBar->msg) {
if (_pinnedBar->msg->getMedia() && _pinnedBar->msg->getMedia()->hasReplyPreview()) { if (_pinnedBar->msg->media() && _pinnedBar->msg->media()->hasReplyPreview()) {
ImagePtr replyPreview = _pinnedBar->msg->getMedia()->replyPreview(); ImagePtr replyPreview = _pinnedBar->msg->media()->replyPreview();
if (!replyPreview->isNull()) { if (!replyPreview->isNull()) {
QRect to(left, top, st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); QRect to(left, top, st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height(), ImageRoundRadius::Small)); p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height(), ImageRoundRadius::Small));

View file

@ -246,7 +246,7 @@ public:
bool touchScroll(const QPoint &delta); bool touchScroll(const QPoint &delta);
void enqueueMessageHighlight(not_null<HistoryItem*> item); void enqueueMessageHighlight(not_null<HistoryView::Element*> view);
TimeMs highlightStartTime(not_null<const HistoryItem*> item) const; TimeMs highlightStartTime(not_null<const HistoryItem*> item) const;
MessageIdsList getSelectedItems() const; MessageIdsList getSelectedItems() const;
@ -673,7 +673,7 @@ private:
// Counts scrollTop for placing the scroll right at the unread // Counts scrollTop for placing the scroll right at the unread
// messages bar, choosing from _history and _migrated unreadBar. // messages bar, choosing from _history and _migrated unreadBar.
int unreadBarTop() const; int unreadBarTop() const;
int itemTopForHighlight(not_null<HistoryItem*> item) const; int itemTopForHighlight(not_null<HistoryView::Element*> view) const;
void scrollToCurrentVoiceMessage(FullMsgId fromId, FullMsgId toId); void scrollToCurrentVoiceMessage(FullMsgId fromId, FullMsgId toId);
// Scroll to current y without updating the _lastUserScrolled time. // Scroll to current y without updating the _lastUserScrolled time.

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_cursor_state.h" #include "history/view/history_view_cursor_state.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/view/history_view_element.h"
HistoryTextState::HistoryTextState(not_null<const HistoryItem*> item) HistoryTextState::HistoryTextState(not_null<const HistoryItem*> item)
: itemId(item->fullId()) { : itemId(item->fullId()) {
@ -31,3 +32,21 @@ HistoryTextState::HistoryTextState(
: itemId(item->fullId()) : itemId(item->fullId())
, link(link) { , link(link) {
} }
HistoryTextState::HistoryTextState(
not_null<const HistoryView::Element*> view)
: HistoryTextState(view->data()) {
}
HistoryTextState::HistoryTextState(
not_null<const HistoryView::Element*> view,
const Text::StateResult &state)
: HistoryTextState(view->data(), state) {
}
HistoryTextState::HistoryTextState(
not_null<const HistoryView::Element*> view,
ClickHandlerPtr link)
: HistoryTextState(view->data(), link) {
}

View file

@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
namespace HistoryView {
class Element;
} // namespace HistoryView
class HistoryItem; class HistoryItem;
enum HistoryCursorState { enum HistoryCursorState {
@ -25,6 +29,13 @@ struct HistoryTextState {
HistoryTextState( HistoryTextState(
not_null<const HistoryItem*> item, not_null<const HistoryItem*> item,
ClickHandlerPtr link); ClickHandlerPtr link);
HistoryTextState(not_null<const HistoryView::Element*> view);
HistoryTextState(
not_null<const HistoryView::Element*> view,
const Text::StateResult &state);
HistoryTextState(
not_null<const HistoryView::Element*> view,
ClickHandlerPtr link);
HistoryTextState( HistoryTextState(
std::nullptr_t, std::nullptr_t,
const Text::StateResult &state) const Text::StateResult &state)
@ -56,7 +67,7 @@ struct HistoryStateRequest {
} }
}; };
enum InfoDisplayType { enum InfoDisplayType : char {
InfoDisplayDefault, InfoDisplayDefault,
InfoDisplayOverImage, InfoDisplayOverImage,
InfoDisplayOverBackground, InfoDisplayOverBackground,

View file

@ -8,10 +8,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "history/history_item_components.h" #include "history/history_item_components.h"
#include "history/history_item.h"
#include "history/history_media.h" #include "history/history_media.h"
#include "history/history_media_grouped.h"
#include "history/history.h" #include "history/history.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_types.h"
#include "auth_session.h" #include "auth_session.h"
#include "layout.h"
namespace HistoryView { namespace HistoryView {
namespace { namespace {
@ -20,15 +24,51 @@ namespace {
constexpr int kAttachMessageToPreviousSecondsDelta = 900; constexpr int kAttachMessageToPreviousSecondsDelta = 900;
} // namespace } // namespace
TextSelection UnshiftItemSelection(
TextSelection selection,
uint16 byLength) {
return (selection == FullSelection)
? selection
: ::unshiftSelection(selection, byLength);
}
TextSelection ShiftItemSelection(
TextSelection selection,
uint16 byLength) {
return (selection == FullSelection)
? selection
: ::shiftSelection(selection, byLength);
}
TextSelection UnshiftItemSelection(
TextSelection selection,
const Text &byText) {
return UnshiftItemSelection(selection, byText.length());
}
TextSelection ShiftItemSelection(
TextSelection selection,
const Text &byText) {
return ShiftItemSelection(selection, byText.length());
}
Element::Element(not_null<HistoryItem*> data, Context context) Element::Element(not_null<HistoryItem*> data, Context context)
: _data(data) : _data(data)
, _media(_data->media() ? _data->media()->createView(this) : nullptr)
, _context(context) { , _context(context) {
Auth().data().registerItemView(this);
initGroup();
} }
not_null<HistoryItem*> Element::data() const { not_null<HistoryItem*> Element::data() const {
return _data; return _data;
} }
HistoryMedia *Element::media() const {
return _media.get();
}
Context Element::context() const { Context Element::context() const {
return _context; return _context;
} }
@ -44,7 +84,7 @@ void Element::setY(int y) {
int Element::marginTop() const { int Element::marginTop() const {
const auto item = data(); const auto item = data();
auto result = 0; auto result = 0;
if (!item->isHiddenByGroup()) { if (!isHiddenByGroup()) {
if (isAttachedToPrevious()) { if (isAttachedToPrevious()) {
result += st::msgMarginTopAttached; result += st::msgMarginTopAttached;
} else { } else {
@ -60,7 +100,11 @@ int Element::marginTop() const {
int Element::marginBottom() const { int Element::marginBottom() const {
const auto item = data(); const auto item = data();
return item->isHiddenByGroup() ? 0 : st::msgMargin.bottom(); return isHiddenByGroup() ? 0 : st::msgMargin.bottom();
}
bool Element::isUnderCursor() const {
return (App::hoveredItem() == this);
} }
void Element::setPendingResize() { void Element::setPendingResize() {
@ -82,6 +126,113 @@ bool Element::isAttachedToNext() const {
return _flags & Flag::AttachedToNext; return _flags & Flag::AttachedToNext;
} }
int Element::skipBlockWidth() const {
return st::msgDateSpace + infoWidth() - st::msgDateDelta.x();
}
int Element::skipBlockHeight() const {
return st::msgDateFont->height - st::msgDateDelta.y();
}
QString Element::skipBlock() const {
return textcmdSkipBlock(skipBlockWidth(), skipBlockHeight());
}
int Element::infoWidth() const {
return 0;
}
bool Element::isHiddenByGroup() const {
return _flags & Flag::HiddenByGroup;
}
void Element::makeGroupMember(not_null<Element*> leader) {
Expects(leader != this);
const auto group = Get<Group>();
Assert(group != nullptr);
if (group->leader == this) {
if (auto single = _media ? _media->takeLastFromGroup() : nullptr) {
_media = std::move(single);
}
_flags |= Flag::HiddenByGroup;
Auth().data().requestViewResize(this);
group->leader = leader;
base::take(group->others);
} else if (group->leader != leader) {
group->leader = leader;
}
Ensures(isHiddenByGroup());
Ensures(group->others.empty());
}
void Element::makeGroupLeader(std::vector<not_null<Element*>> &&others) {
const auto group = Get<Group>();
Assert(group != nullptr);
const auto leaderChanged = (group->leader != this);
if (leaderChanged) {
group->leader = this;
_flags &= ~Flag::HiddenByGroup;
Auth().data().requestViewResize(this);
}
group->others = std::move(others);
if (!_media || !_media->applyGroup(group->others)) {
resetGroupMedia(group->others);
data()->invalidateChatsListEntry();
}
Ensures(!isHiddenByGroup());
}
bool Element::groupIdValidityChanged() {
if (Has<Group>()) {
if (_media && _media->canBeGrouped()) {
return false;
}
RemoveComponents(Group::Bit());
Auth().data().requestViewResize(this);
return true;
}
return false;
}
void Element::validateGroupId() {
// Just ignore the result.
groupIdValidityChanged();
}
Group *Element::getFullGroup() {
if (const auto group = Get<Group>()) {
if (group->leader == this) {
return group;
}
return group->leader->Get<Group>();
}
return nullptr;
}
void Element::initGroup() {
if (const auto groupId = _data->groupId()) {
AddComponents(Group::Bit());
const auto group = Get<Group>();
group->groupId = groupId;
group->leader = this;
}
}
void Element::resetGroupMedia(
const std::vector<not_null<Element*>> &others) {
if (!others.empty()) {
_media = std::make_unique<HistoryGroupedMedia>(this, others);
} else if (_media) {
_media = _media->takeLastFromGroup();
}
Auth().data().requestViewResize(this);
}
void Element::previousInBlocksChanged() { void Element::previousInBlocksChanged() {
recountDisplayDateInBlocks(); recountDisplayDateInBlocks();
recountAttachToPreviousInBlocks(); recountAttachToPreviousInBlocks();
@ -92,6 +243,17 @@ void Element::nextInBlocksChanged() {
setAttachToNext(false); setAttachToNext(false);
} }
void Element::refreshDataId() {
if (const auto media = this->media()) {
media->refreshParentId(this);
if (const auto group = Get<Group>()) {
if (group->leader != this) {
group->leader->refreshDataId();
}
}
}
}
bool Element::computeIsAttachToPrevious(not_null<Element*> previous) { bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
const auto item = data(); const auto item = data();
if (!item->Has<HistoryMessageDate>() && !item->Has<HistoryMessageUnreadBar>()) { if (!item->Has<HistoryMessageDate>() && !item->Has<HistoryMessageUnreadBar>()) {
@ -196,6 +358,53 @@ bool Element::displayFromName() const {
return false; return false;
} }
bool Element::displayForwardedFrom() const {
return false;
}
bool Element::hasOutLayout() const {
return false;
}
bool Element::drawBubble() const {
return false;
}
bool Element::hasBubble() const {
return false;
}
bool Element::hasFastReply() const {
return false;
}
bool Element::displayFastReply() const {
return false;
}
bool Element::displayRightAction() const {
return false;
}
void Element::drawRightAction(
Painter &p,
int left,
int top,
int outerWidth) const {
}
ClickHandlerPtr Element::rightActionLink() const {
return ClickHandlerPtr();
}
bool Element::displayEditedBadge() const {
return false;
}
QDateTime Element::displayedEditDate() const {
return QDateTime();
}
HistoryBlock *Element::block() { HistoryBlock *Element::block() {
return _block; return _block;
} }
@ -261,6 +470,29 @@ Element *Element::nextInBlocks() const {
return nullptr; return nullptr;
} }
void Element::drawInfo(
Painter &p,
int right,
int bottom,
int width,
bool selected,
InfoDisplayType type) const {
}
bool Element::pointInTime(
int right,
int bottom,
QPoint point,
InfoDisplayType type) const {
return false;
}
TextSelection Element::adjustSelection(
TextSelection selection,
TextSelectType type) const {
return selection;
}
void Element::clickHandlerActiveChanged( void Element::clickHandlerActiveChanged(
const ClickHandlerPtr &handler, const ClickHandlerPtr &handler,
bool active) { bool active) {
@ -271,7 +503,7 @@ void Element::clickHandlerActiveChanged(
} }
App::hoveredLinkItem(active ? this : nullptr); App::hoveredLinkItem(active ? this : nullptr);
Auth().data().requestItemRepaint(_data); Auth().data().requestItemRepaint(_data);
if (const auto media = _data->getMedia()) { if (const auto media = this->media()) {
media->clickHandlerActiveChanged(handler, active); media->clickHandlerActiveChanged(handler, active);
} }
} }
@ -286,13 +518,13 @@ void Element::clickHandlerPressedChanged(
} }
App::pressedLinkItem(pressed ? this : nullptr); App::pressedLinkItem(pressed ? this : nullptr);
Auth().data().requestItemRepaint(_data); Auth().data().requestItemRepaint(_data);
if (const auto media = _data->getMedia()) { if (const auto media = this->media()) {
media->clickHandlerPressedChanged(handler, pressed); media->clickHandlerPressedChanged(handler, pressed);
} }
} }
Element::~Element() { Element::~Element() {
App::messageViewDestroyed(this); Auth().data().unregisterItemView(this);
} }
} // namespace HistoryView } // namespace HistoryView

View file

@ -13,11 +13,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class HistoryBlock; class HistoryBlock;
class HistoryItem; class HistoryItem;
class HistoryMedia;
class HistoryWebPage;
struct HistoryTextState; struct HistoryTextState;
struct HistoryStateRequest; struct HistoryStateRequest;
enum InfoDisplayType : char;
namespace HistoryView { namespace HistoryView {
TextSelection UnshiftItemSelection(
TextSelection selection,
uint16 byLength);
TextSelection ShiftItemSelection(
TextSelection selection,
uint16 byLength);
TextSelection UnshiftItemSelection(
TextSelection selection,
const Text &byText);
TextSelection ShiftItemSelection(
TextSelection selection,
const Text &byText);
class Element;
struct Group : public RuntimeComponent<Group, Element> {
MessageGroupId groupId = MessageGroupId::None;
Element *leader = nullptr;
std::vector<not_null<Element*>> others;
};
enum class Context : char { enum class Context : char {
History, History,
Feed, Feed,
@ -26,7 +49,7 @@ enum class Context : char {
class Element class Element
: public Object : public Object
// , public RuntimeComposer , public RuntimeComposer<Element>
, public ClickHandlerHost { , public ClickHandlerHost {
public: public:
Element(not_null<HistoryItem*> data, Context context); Element(not_null<HistoryItem*> data, Context context);
@ -35,12 +58,15 @@ public:
NeedsResize = 0x01, NeedsResize = 0x01,
AttachedToPrevious = 0x02, AttachedToPrevious = 0x02,
AttachedToNext = 0x04, AttachedToNext = 0x04,
HiddenByGroup = 0x08,
}; };
using Flags = base::flags<Flag>; using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; } friend inline constexpr auto is_flag_type(Flag) { return true; }
not_null<HistoryItem*> data() const; not_null<HistoryItem*> data() const;
HistoryMedia *media() const;
Context context() const; Context context() const;
void refreshDataId();
int y() const; int y() const;
void setY(int y); void setY(int y);
@ -49,10 +75,23 @@ public:
int marginBottom() const; int marginBottom() const;
void setPendingResize(); void setPendingResize();
bool pendingResize() const; bool pendingResize() const;
bool isUnderCursor() const;
bool isAttachedToPrevious() const; bool isAttachedToPrevious() const;
bool isAttachedToNext() const; bool isAttachedToNext() const;
int skipBlockWidth() const;
int skipBlockHeight() const;
QString skipBlock() const;
virtual int infoWidth() const;
bool isHiddenByGroup() const;
void makeGroupMember(not_null<Element*> leader);
void makeGroupLeader(std::vector<not_null<Element*>> &&others);
bool groupIdValidityChanged();
void validateGroupId();
Group *getFullGroup();
// For blocks context this should be called only from recountAttachToPreviousInBlocks(). // For blocks context this should be called only from recountAttachToPreviousInBlocks().
void setAttachToPrevious(bool attachToNext); void setAttachToPrevious(bool attachToNext);
@ -75,6 +114,23 @@ public:
QPoint point, QPoint point,
HistoryStateRequest request) const = 0; HistoryStateRequest request) const = 0;
virtual void updatePressed(QPoint point) = 0; virtual void updatePressed(QPoint point) = 0;
virtual void drawInfo(
Painter &p,
int right,
int bottom,
int width,
bool selected,
InfoDisplayType type) const;
virtual bool pointInTime(
int right,
int bottom,
QPoint point,
InfoDisplayType type) const;
virtual TextWithEntities selectedText(
TextSelection selection) const = 0;
[[nodiscard]] virtual TextSelection adjustSelection(
TextSelection selection,
TextSelectType type) const;
// ClickHandlerHost interface. // ClickHandlerHost interface.
void clickHandlerActiveChanged( void clickHandlerActiveChanged(
@ -86,10 +142,25 @@ public:
// hasFromPhoto() returns true even if we don't display the photo // hasFromPhoto() returns true even if we don't display the photo
// but we need to skip a place at the left side for this photo // but we need to skip a place at the left side for this photo
virtual bool displayFromPhoto() const;
virtual bool hasFromPhoto() const; virtual bool hasFromPhoto() const;
virtual bool displayFromPhoto() const;
virtual bool hasFromName() const; virtual bool hasFromName() const;
virtual bool displayFromName() const; virtual bool displayFromName() const;
virtual bool displayForwardedFrom() const;
virtual bool hasOutLayout() const;
virtual bool drawBubble() const;
virtual bool hasBubble() const;
virtual bool hasFastReply() const;
virtual bool displayFastReply() const;
virtual bool displayRightAction() const;
virtual void drawRightAction(
Painter &p,
int left,
int top,
int outerWidth) const;
virtual ClickHandlerPtr rightActionLink() const;
virtual bool displayEditedBadge() const;
virtual QDateTime displayedEditDate() const;
// Legacy blocks structure. // Legacy blocks structure.
HistoryBlock *block(); HistoryBlock *block();
@ -126,7 +197,12 @@ private:
virtual QSize performCountOptimalSize() = 0; virtual QSize performCountOptimalSize() = 0;
virtual QSize performCountCurrentSize(int newWidth) = 0; virtual QSize performCountCurrentSize(int newWidth) = 0;
void initGroup();
void resetGroupMedia(const std::vector<not_null<Element*>> &others);
const not_null<HistoryItem*> _data; const not_null<HistoryItem*> _data;
std::unique_ptr<HistoryMedia> _media;
int _y = 0; int _y = 0;
Context _context; Context _context;

View file

@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/edit_participant_box.h" #include "boxes/edit_participant_box.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_feed.h" #include "data/data_feed.h"
#include "data/data_media_types.h"
#include "styles/style_history.h" #include "styles/style_history.h"
namespace HistoryView { namespace HistoryView {
@ -655,7 +656,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
TextWithEntities ListWidget::getSelectedText() const { TextWithEntities ListWidget::getSelectedText() const {
return _selectedItem return _selectedItem
? _selectedItem->data()->selectedText(_selectedText) ? _selectedItem->selectedText(_selectedText)
: TextWithEntities(); : TextWithEntities();
} }
@ -825,7 +826,7 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
const auto item = view ? view->data().get() : nullptr; const auto item = view ? view->data().get() : nullptr;
const auto itemId = item ? item->fullId() : FullMsgId(); const auto itemId = item ? item->fullId() : FullMsgId();
bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg()); bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg());
bool canForward = item && item->canForward(); bool canForward = item && item->allowsForward();
auto msg = dynamic_cast<HistoryMessage*>(item); auto msg = dynamic_cast<HistoryMessage*>(item);
if (isUponSelected > 0) { if (isUponSelected > 0) {
@ -833,7 +834,7 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
} else { } else {
if (item && !isUponSelected) { if (item && !isUponSelected) {
auto mediaHasTextForCopy = false; auto mediaHasTextForCopy = false;
if (auto media = (msg ? msg->getMedia() : nullptr)) { if (auto media = view->media()) {
mediaHasTextForCopy = media->hasTextForCopy(); mediaHasTextForCopy = media->hasTextForCopy();
if (media->type() == MediaTypeWebPage && static_cast<HistoryWebPage*>(media)->attach()) { if (media->type() == MediaTypeWebPage && static_cast<HistoryWebPage*>(media)->attach()) {
media = static_cast<HistoryWebPage*>(media)->attach(); media = static_cast<HistoryWebPage*>(media)->attach();
@ -954,8 +955,8 @@ void ListWidget::showContextInFolder(not_null<DocumentData*> document) {
void ListWidget::openContextGif(FullMsgId itemId) { void ListWidget::openContextGif(FullMsgId itemId) {
if (const auto item = App::histItemById(itemId)) { if (const auto item = App::histItemById(itemId)) {
if (auto media = item->getMedia()) { if (auto media = item->media()) {
if (auto document = media->getDocument()) { if (auto document = media->document()) {
Messenger::Instance().showDocument(document, item); Messenger::Instance().showDocument(document, item);
} }
} }
@ -964,13 +965,10 @@ void ListWidget::openContextGif(FullMsgId itemId) {
void ListWidget::copyContextText(FullMsgId itemId) { void ListWidget::copyContextText(FullMsgId itemId) {
if (const auto item = App::histItemById(itemId)) { if (const auto item = App::histItemById(itemId)) {
if (const auto media = item->getMedia()) { if (const auto view = viewForItem(item)) {
if (media->type() == MediaTypeSticker) { setToClipboard(view->selectedText(FullSelection));
return;
} }
} }
setToClipboard(item->selectedText(FullSelection));
}
} }
void ListWidget::setToClipboard( void ListWidget::setToClipboard(
@ -1243,7 +1241,7 @@ void ListWidget::updateSelected() {
} }
auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) }; auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) };
if (_mouseSelectType != TextSelectType::Letters) { if (_mouseSelectType != TextSelectType::Letters) {
selection = _mouseActionItem->data()->adjustSelection(selection, _mouseSelectType); selection = _mouseActionItem->adjustSelection(selection, _mouseSelectType);
} }
if (_selectedText != selection) { if (_selectedText != selection) {
_selectedText = selection; _selectedText = selection;
@ -1342,16 +1340,20 @@ void ListWidget::performDrag() {
// auto forwardMimeType = QString(); // auto forwardMimeType = QString();
// auto pressedMedia = static_cast<HistoryMedia*>(nullptr); // auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
// if (auto pressedItem = App::pressedItem()) { // if (auto pressedItem = App::pressedItem()) {
// pressedMedia = pressedItem->getMedia(); // pressedMedia = pressedItem->media();
// if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) { // if (_mouseCursorState == HistoryInDateCursorState
// Auth().data().setMimeForwardIds(Auth().data().itemOrItsGroup(pressedItem)); // || (pressedMedia && pressedMedia->dragItem())) {
// Auth().data().setMimeForwardIds(
// Auth().data().itemOrItsGroup(pressedItem->data()));
// forwardMimeType = qsl("application/x-td-forward"); // forwardMimeType = qsl("application/x-td-forward");
// } // }
// } // }
// if (auto pressedLnkItem = App::pressedLinkItem()) { // if (auto pressedLnkItem = App::pressedLinkItem()) {
// if ((pressedMedia = pressedLnkItem->getMedia())) { // if ((pressedMedia = pressedLnkItem->media())) {
// if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) { // if (forwardMimeType.isEmpty()
// Auth().data().setMimeForwardIds({ 1, pressedLnkItem->fullId() }); // && pressedMedia->dragItemByHandler(pressedHandler)) {
// Auth().data().setMimeForwardIds(
// { 1, pressedLnkItem->fullId() });
// forwardMimeType = qsl("application/x-td-forward"); // forwardMimeType = qsl("application/x-td-forward");
// } // }
// } // }

File diff suppressed because it is too large Load diff

View file

@ -10,9 +10,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
class HistoryMessage; class HistoryMessage;
struct HistoryMessageEdited;
namespace HistoryView { namespace HistoryView {
// Special type of Component for the channel actions log.
struct LogEntryOriginal
: public RuntimeComponent<LogEntryOriginal, Element> {
LogEntryOriginal();
LogEntryOriginal(LogEntryOriginal &&other);
LogEntryOriginal &operator=(LogEntryOriginal &&other);
~LogEntryOriginal();
std::unique_ptr<HistoryWebPage> page;
};
class Message : public Element { class Message : public Element {
public: public:
Message(not_null<HistoryMessage*> data, Context context); Message(not_null<HistoryMessage*> data, Context context);
@ -27,20 +40,58 @@ public:
QPoint point, QPoint point,
HistoryStateRequest request) const override; HistoryStateRequest request) const override;
void updatePressed(QPoint point) override; void updatePressed(QPoint point) override;
void drawInfo(
Painter &p,
int right,
int bottom,
int width,
bool selected,
InfoDisplayType type) const override;
bool pointInTime(
int right,
int bottom,
QPoint point,
InfoDisplayType type) const override;
TextWithEntities selectedText(TextSelection selection) const override;
TextSelection adjustSelection(
TextSelection selection,
TextSelectType type) const override;
// hasFromPhoto() returns true even if we don't display the photo // hasFromPhoto() returns true even if we don't display the photo
// but we need to skip a place at the left side for this photo // but we need to skip a place at the left side for this photo
bool displayFromPhoto() const override;
bool hasFromPhoto() const override; bool hasFromPhoto() const override;
bool displayFromPhoto() const override;
bool hasFromName() const override; bool hasFromName() const override;
bool displayFromName() const override; bool displayFromName() const override;
bool displayForwardedFrom() const override;
bool hasOutLayout() const override;
bool drawBubble() const override;
bool hasBubble() const override;
bool hasFastReply() const override;
bool displayFastReply() const override;
bool displayRightAction() const override;
void drawRightAction(
Painter &p,
int left,
int top,
int outerWidth) const override;
ClickHandlerPtr rightActionLink() const override;
bool displayEditedBadge() const override;
QDateTime displayedEditDate() const override;
int infoWidth() const override;
private: private:
not_null<HistoryMessage*> message() const; not_null<HistoryMessage*> message() const;
void initLogEntryOriginal();
void refreshEditedBadge();
void fromNameUpdated(int width) const; void fromNameUpdated(int width) const;
[[nodiscard]] TextSelection skipTextSelection(
TextSelection selection) const;
[[nodiscard]] TextSelection unskipTextSelection(
TextSelection selection) const;
void paintFromName(Painter &p, QRect &trect, bool selected) const; void paintFromName(Painter &p, QRect &trect, bool selected) const;
void paintForwardedInfo(Painter &p, QRect &trect, bool selected) const; void paintForwardedInfo(Painter &p, QRect &trect, bool selected) const;
void paintReplyInfo(Painter &p, QRect &trect, bool selected) const; void paintReplyInfo(Painter &p, QRect &trect, bool selected) const;
@ -78,6 +129,20 @@ private:
QSize performCountOptimalSize() override; QSize performCountOptimalSize() override;
QSize performCountCurrentSize(int newWidth) override; QSize performCountCurrentSize(int newWidth) override;
bool displayFastShare() const;
bool displayGoToOriginal() const;
ClickHandlerPtr fastReplyLink() const;
QDateTime displayedEditDate(bool hasViaBotOrInlineMarkup) const;
const HistoryMessageEdited *displayedEditBadge() const;
HistoryMessageEdited *displayedEditBadge();
void initTime();
int timeLeft() const;
HistoryWebPage *logEntryOriginal() const;
mutable ClickHandlerPtr _rightActionLink;
mutable ClickHandlerPtr _fastReplyLink;
}; };
} // namespace HistoryView } // namespace HistoryView

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_abstract_structure.h" #include "data/data_abstract_structure.h"
#include "styles/style_history.h" #include "styles/style_history.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "layout.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
namespace HistoryView { namespace HistoryView {
@ -309,7 +310,7 @@ QRect Service::countGeometry() const {
QSize Service::performCountCurrentSize(int newWidth) { QSize Service::performCountCurrentSize(int newWidth) {
const auto item = message(); const auto item = message();
const auto media = item->getMedia(); const auto media = this->media();
auto newHeight = item->displayedDateHeight(); auto newHeight = item->displayedDateHeight();
if (auto unreadbar = item->Get<HistoryMessageUnreadBar>()) { if (auto unreadbar = item->Get<HistoryMessageUnreadBar>()) {
@ -349,7 +350,7 @@ QSize Service::performCountCurrentSize(int newWidth) {
QSize Service::performCountOptimalSize() { QSize Service::performCountOptimalSize() {
const auto item = message(); const auto item = message();
const auto media = item->getMedia(); const auto media = this->media();
auto maxWidth = item->_text.maxWidth() + st::msgServicePadding.left() + st::msgServicePadding.right(); auto maxWidth = item->_text.maxWidth() + st::msgServicePadding.left() + st::msgServicePadding.right();
auto minHeight = item->_text.minHeight(); auto minHeight = item->_text.minHeight();
@ -409,11 +410,11 @@ void Service::draw(
p.setTextPalette(st::serviceTextPalette); p.setTextPalette(st::serviceTextPalette);
if (auto media = item->getMedia()) { if (auto media = this->media()) {
height -= st::msgServiceMargin.top() + media->height(); height -= st::msgServiceMargin.top() + media->height();
auto left = st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.top(); auto left = st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.top();
p.translate(left, top); p.translate(left, top);
media->draw(p, clip.translated(-left, -top), item->skipTextSelection(selection), ms); media->draw(p, clip.translated(-left, -top), TextSelection(), ms);
p.translate(-left, -top); p.translate(-left, -top);
} }
@ -435,7 +436,7 @@ void Service::draw(
bool Service::hasPoint(QPoint point) const { bool Service::hasPoint(QPoint point) const {
const auto item = message(); const auto item = message();
const auto media = item->getMedia(); const auto media = this->media();
auto g = countGeometry(); auto g = countGeometry();
if (g.width() < 1) { if (g.width() < 1) {
@ -456,7 +457,7 @@ bool Service::hasPoint(QPoint point) const {
HistoryTextState Service::getState(QPoint point, HistoryStateRequest request) const { HistoryTextState Service::getState(QPoint point, HistoryStateRequest request) const {
const auto item = message(); const auto item = message();
const auto media = item->getMedia(); const auto media = this->media();
auto result = HistoryTextState(item); auto result = HistoryTextState(item);
@ -504,4 +505,15 @@ HistoryTextState Service::getState(QPoint point, HistoryStateRequest request) co
void Service::updatePressed(QPoint point) { void Service::updatePressed(QPoint point) {
} }
TextWithEntities Service::selectedText(TextSelection selection) const {
return message()->_text.originalTextWithEntities(
(selection == FullSelection) ? AllTextSelection : selection);
}
TextSelection Service::adjustSelection(
TextSelection selection,
TextSelectType type) const {
return message()->_text.adjustSelection(selection, type);
}
} // namespace HistoryView } // namespace HistoryView

View file

@ -27,6 +27,10 @@ public:
QPoint point, QPoint point,
HistoryStateRequest request) const override; HistoryStateRequest request) const override;
void updatePressed(QPoint point) override; void updatePressed(QPoint point) override;
TextWithEntities selectedText(TextSelection selection) const override;
TextSelection adjustSelection(
TextSelection selection,
TextSelectType type) const override;
private: private:
not_null<HistoryService*> message() const; not_null<HistoryService*> message() const;

View file

@ -9,7 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/info_controller.h" #include "info/info_controller.h"
#include "overview/overview_layout.h" #include "overview/overview_layout.h"
#include "history/history_media_types.h" #include "data/data_media_types.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "data/data_session.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history.h" #include "history/history.h"
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
@ -27,7 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
#include "core/file_utilities.h" #include "core/file_utilities.h"
#include "data/data_session.h"
namespace Layout = Overview::Layout; namespace Layout = Overview::Layout;
@ -837,14 +839,14 @@ std::unique_ptr<BaseLayout> ListWidget::createLayout(
return nullptr; return nullptr;
} }
auto getPhoto = [&]() -> PhotoData* { auto getPhoto = [&]() -> PhotoData* {
if (const auto media = item->getMedia()) { if (const auto media = item->media()) {
return media->getPhoto(); return media->photo();
} }
return nullptr; return nullptr;
}; };
auto getFile = [&]() -> DocumentData* { auto getFile = [&]() -> DocumentData* {
if (auto media = item->getMedia()) { if (auto media = item->media()) {
return media->getDocument(); return media->document();
} }
return nullptr; return nullptr;
}; };
@ -878,7 +880,7 @@ std::unique_ptr<BaseLayout> ListWidget::createLayout(
} }
return nullptr; return nullptr;
case Type::Link: case Type::Link:
return std::make_unique<Link>(item, item->getMedia()); return std::make_unique<Link>(item, item->media());
case Type::RoundFile: case Type::RoundFile:
return nullptr; return nullptr;
} }
@ -1313,7 +1315,7 @@ void ListWidget::showContextMenu(
})); }));
} else { } else {
if (overSelected != SelectionState::NotOverSelectedItems) { if (overSelected != SelectionState::NotOverSelectedItems) {
if (item->canForward()) { if (item->allowsForward()) {
_contextMenu->addAction( _contextMenu->addAction(
lang(lng_context_forward_msg), lang(lng_context_forward_msg),
base::lambda_guarded(this, [this, universalId] { base::lambda_guarded(this, [this, universalId] {
@ -1497,7 +1499,7 @@ bool ListWidget::changeItemSelection(
return false; return false;
} }
iterator->second.canDelete = item->canDelete(); iterator->second.canDelete = item->canDelete();
iterator->second.canForward = item->canForward(); iterator->second.canForward = item->allowsForward();
return true; return true;
} }
return changeExisting(iterator); return changeExisting(iterator);

View file

@ -78,7 +78,9 @@ public:
}; };
class LayoutItemBase : public RuntimeComposer, public ClickHandlerHost { class LayoutItemBase
: public RuntimeComposer<LayoutItemBase>
, public ClickHandlerHost {
public: public:
LayoutItemBase() { LayoutItemBase() {
} }

View file

@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
#include "data/data_drafts.h" #include "data/data_drafts.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_types.h"
#include "ui/special_buttons.h" #include "ui/special_buttons.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.h"
@ -327,8 +328,8 @@ void MainWidget::checkCurrentFloatPlayer() {
last->widget->detach(); last->widget->detach();
} }
if (auto item = App::histItemById(fullId)) { if (auto item = App::histItemById(fullId)) {
if (auto media = item->getMedia()) { if (auto media = item->media()) {
if (auto document = media->getDocument()) { if (auto document = media->document()) {
if (document->isVideoMessage()) { if (document->isVideoMessage()) {
_playerFloats.push_back(std::make_unique<Float>(this, item, [this](not_null<Float*> instance, bool visible) { _playerFloats.push_back(std::make_unique<Float>(this, item, [this](not_null<Float*> instance, bool visible) {
instance->hiddenByWidget = !visible; instance->hiddenByWidget = !visible;
@ -4908,11 +4909,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
if (existing) { if (existing) {
existing->destroy(); existing->destroy();
} }
App::historyUnregItem(local);
Auth().messageIdChanging.notify({ local, newId }, true);
local->setRealId(d.vid.v); local->setRealId(d.vid.v);
App::historyRegItem(local);
Auth().data().requestItemRepaint(local);
} }
} }
App::historyUnregRandom(d.vrandom_id.v); App::historyUnregRandom(d.vrandom_id.v);

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/merge.h> #include <rpl/merge.h>
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_types.h"
#include "history/history_media.h" #include "history/history_media.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "media/media_clip_reader.h" #include "media/media_clip_reader.h"
@ -31,10 +32,10 @@ Float::Float(
, _item(item) , _item(item)
, _toggleCallback(std::move(toggleCallback)) , _toggleCallback(std::move(toggleCallback))
, _draggedCallback(std::move(draggedCallback)) { , _draggedCallback(std::move(draggedCallback)) {
auto media = _item->getMedia(); auto media = _item->media();
Assert(media != nullptr); Assert(media != nullptr);
auto document = media->getDocument(); auto document = media->document();
Assert(document != nullptr); Assert(document != nullptr);
Assert(document->isVideoMessage()); Assert(document->isVideoMessage());
@ -100,9 +101,10 @@ float64 Float::outRatio() const {
void Float::mouseReleaseEvent(QMouseEvent *e) { void Float::mouseReleaseEvent(QMouseEvent *e) {
if (_down) { if (_down) {
_down = false; _down = false;
if (auto media = _item ? _item->getMedia() : nullptr) { // #TODO float player
media->playInline(); //if (auto media = _item ? _item->getMedia() : nullptr) {
} // media->playInline();
//}
} }
if (_drag) { if (_drag) {
finishDrag(outRatio() < 0.5); finishDrag(outRatio() < 0.5);
@ -119,9 +121,10 @@ void Float::finishDrag(bool closed) {
void Float::mouseDoubleClickEvent(QMouseEvent *e) { void Float::mouseDoubleClickEvent(QMouseEvent *e) {
if (_item) { if (_item) {
// Handle second click. // Handle second click.
if (auto media = _item->getMedia()) { // #TODO float player
media->playInline(); //if (auto media = _item->getMedia()) {
} // media->playInline();
//}
Ui::showPeerHistoryAtItem(_item); Ui::showPeerHistoryAtItem(_item);
} }
} }
@ -191,13 +194,14 @@ void Float::paintEvent(QPaintEvent *e) {
} }
Clip::Reader *Float::getReader() const { Clip::Reader *Float::getReader() const {
if (auto media = _item ? _item->getMedia() : nullptr) { // #TODO float player
if (auto reader = media->getClipReader()) { //if (auto media = _item ? _item->getMedia() : nullptr) {
if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) { // if (auto reader = media->getClipReader()) {
return reader; // if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) {
} // return reader;
} // }
} // }
//}
return nullptr; return nullptr;
} }

View file

@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_media.h" #include "data/data_media_types.h"
namespace Media { namespace Media {
namespace Player { namespace Player {
@ -222,8 +222,8 @@ bool Instance::moveInPlaylist(
} }
const auto newIndex = *data->playlistIndex + delta; const auto newIndex = *data->playlistIndex + delta;
if (const auto item = itemByIndex(data, newIndex)) { if (const auto item = itemByIndex(data, newIndex)) {
if (const auto media = item->getMedia()) { if (const auto media = item->media()) {
if (const auto document = media->getDocument()) { if (const auto document = media->document()) {
if (autonext) { if (autonext) {
_switchToNextNotifier.notify({ _switchToNextNotifier.notify({
data->current, data->current,
@ -300,11 +300,12 @@ void Instance::play(const AudioMsgId &audioId) {
documentLoadProgress(document); documentLoadProgress(document);
} }
} else if (document->isVideoMessage()) { } else if (document->isVideoMessage()) {
if (const auto item = App::histItemById(audioId.contextId())) { // #TODO float player
if (const auto media = item->getMedia()) { //if (const auto item = App::histItemById(audioId.contextId())) {
media->playInline(); // if (const auto media = item->getMedia()) {
} // media->playInline();
} // }
//}
} }
} }
@ -429,8 +430,8 @@ void Instance::preloadNext(not_null<Data*> data) {
} }
const auto nextIndex = *data->playlistIndex + 1; const auto nextIndex = *data->playlistIndex + 1;
if (const auto item = itemByIndex(data, nextIndex)) { if (const auto item = itemByIndex(data, nextIndex)) {
if (const auto media = item->getMedia()) { if (const auto media = item->media()) {
if (const auto document = media->getDocument()) { if (const auto document = media->document()) {
const auto isLoaded = document->loaded( const auto isLoaded = document->loaded(
DocumentData::FilePathResolveSaveFromDataSilent); DocumentData::FilePathResolveSaveFromDataSilent);
if (!isLoaded) { if (!isLoaded) {

View file

@ -12,8 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/media/info_media_list_widget.h" #include "info/media/info_media_list_widget.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_media.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_media_types.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
#include "mainwindow.h" #include "mainwindow.h"
@ -258,8 +258,8 @@ void Panel::refreshList() {
const auto contextId = current.contextId(); const auto contextId = current.contextId();
const auto peer = [&]() -> PeerData* { const auto peer = [&]() -> PeerData* {
const auto item = contextId ? App::histItemById(contextId) : nullptr; const auto item = contextId ? App::histItemById(contextId) : nullptr;
const auto media = item ? item->getMedia() : nullptr; const auto media = item ? item->media() : nullptr;
const auto document = media ? media->getDocument() : nullptr; const auto document = media ? media->document() : nullptr;
if (!document || !document->isSharedMediaMusic()) { if (!document || !document->isSharedMediaMusic()) {
return nullptr; return nullptr;
} }

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user_photos.h" #include "data/data_user_photos.h"
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_media_types.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_media.h" #include "history/history_media.h"
#include "styles/style_mediaview.h" #include "styles/style_mediaview.h"
@ -473,10 +474,10 @@ auto GroupThumbs::createThumb(Key key) -> std::unique_ptr<Thumb> {
return createThumb(key, photo->date ? photo->thumb : ImagePtr()); return createThumb(key, photo->date ? photo->thumb : ImagePtr());
} else if (const auto msgId = base::get_if<FullMsgId>(&key)) { } else if (const auto msgId = base::get_if<FullMsgId>(&key)) {
if (const auto item = App::histItemById(*msgId)) { if (const auto item = App::histItemById(*msgId)) {
if (const auto media = item->getMedia()) { if (const auto media = item->media()) {
if (const auto photo = media->getPhoto()) { if (const auto photo = media->photo()) {
return createThumb(key, photo->thumb); return createThumb(key, photo->thumb);
} else if (const auto document = media->getDocument()) { } else if (const auto document = media->document()) {
return createThumb(key, document->thumb); return createThumb(key, document->thumb);
} }
} }

View file

@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "history/history_message.h" #include "history/history_message.h"
#include "history/history_media_types.h" #include "history/history_media_types.h"
#include "data/data_media_types.h"
#include "window/themes/window_theme_preview.h" #include "window/themes/window_theme_preview.h"
#include "window/window_peer_menu.h" #include "window/window_peer_menu.h"
#include "observer_peer.h" #include "observer_peer.h"
@ -1236,13 +1237,13 @@ void MediaView::refreshMediaViewer() {
void MediaView::refreshCaption(HistoryItem *item) { void MediaView::refreshCaption(HistoryItem *item) {
_caption = Text(); _caption = Text();
const auto media = item ? item->getMedia() : nullptr; const auto media = item ? item->media() : nullptr;
if (!media) { if (!media) {
return; return;
} }
const auto caption = media->getCaption(); const auto caption = media->caption();
if (caption.text.isEmpty()) { if (caption.isEmpty()) {
return; return;
} }
const auto asBot = [&] { const auto asBot = [&] {
@ -1254,7 +1255,7 @@ void MediaView::refreshCaption(HistoryItem *item) {
_caption = Text(st::msgMinWidth); _caption = Text(st::msgMinWidth);
_caption.setMarkedText( _caption.setMarkedText(
st::mediaviewCaptionStyle, st::mediaviewCaptionStyle,
caption, { caption, EntitiesInText() }, // #TODO caption entities parse
Ui::ItemTextOptions(item)); Ui::ItemTextOptions(item));
} }
@ -2448,10 +2449,10 @@ MediaView::Entity MediaView::entityForSharedMedia(int index) const {
MediaView::Entity MediaView::entityForItemId(const FullMsgId &itemId) const { MediaView::Entity MediaView::entityForItemId(const FullMsgId &itemId) const {
if (const auto item = App::histItemById(itemId)) { if (const auto item = App::histItemById(itemId)) {
if (const auto media = item->getMedia()) { if (const auto media = item->media()) {
if (const auto photo = media->getPhoto()) { if (const auto photo = media->photo()) {
return { photo, item }; return { photo, item };
} else if (const auto document = media->getDocument()) { } else if (const auto document = media->document()) {
return { document, item }; return { document, item };
} }
} }
@ -2474,7 +2475,7 @@ void MediaView::setContext(base::optional_variant<
not_null<PeerData*>> context) { not_null<PeerData*>> context) {
if (auto item = base::get_if<not_null<HistoryItem*>>(&context)) { if (auto item = base::get_if<not_null<HistoryItem*>>(&context)) {
_msgid = (*item)->fullId(); _msgid = (*item)->fullId();
_canForwardItem = (*item)->canForward(); _canForwardItem = (*item)->allowsForward();
_canDeleteItem = (*item)->canDelete(); _canDeleteItem = (*item)->canDelete();
_history = (*item)->history(); _history = (*item)->history();
_peer = _history->peer; _peer = _history->peer;

View file

@ -68,8 +68,8 @@ enum class MTPDmessage_ClientFlag : uint32 {
// message has an admin badge in supergroup // message has an admin badge in supergroup
f_has_admin_badge = (1U << 20), f_has_admin_badge = (1U << 20),
// message is not displayed because it is part of a group //// message is not displayed because it is part of a group
f_hidden_by_group = (1U << 19), //f_hidden_by_group = (1U << 19),
// update this when adding new client side flags // update this when adding new client side flags
MIN_FIELD = (1U << 19), MIN_FIELD = (1U << 19),

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_web_page.h" #include "data/data_web_page.h"
#include "data/data_media_types.h"
#include "styles/style_overview.h" #include "styles/style_overview.h"
#include "styles/style_history.h" #include "styles/style_history.h"
#include "core/file_utilities.h" #include "core/file_utilities.h"
@ -24,7 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/player/media_player_instance.h" #include "media/player/media_player_instance.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_media_types.h"
#include "history/history_item_components.h" #include "history/history_item_components.h"
#include "ui/effects/round_checkbox.h" #include "ui/effects/round_checkbox.h"
#include "ui/text_options.h" #include "ui/text_options.h"
@ -1165,7 +1165,7 @@ bool Document::updateStatusText() {
Link::Link( Link::Link(
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,
HistoryMedia *media) Data::Media *media)
: ItemBase(parent) { : ItemBase(parent) {
AddComponents(Info::Bit()); AddComponents(Info::Bit());
@ -1211,9 +1211,10 @@ Link::Link(
} }
} }
_page = (media && media->type() == MediaTypeWebPage) // #TODO webpage
? static_cast<HistoryWebPage*>(media)->webpage().get() //_page = (media && media->type() == MediaTypeWebPage)
: nullptr; // ? static_cast<HistoryWebPage*>(media)->webpage().get()
// : nullptr;
if (_page) { if (_page) {
mainUrl = _page->url; mainUrl = _page->url;
if (_page->document) { if (_page->document) {

View file

@ -20,6 +20,10 @@ namespace style {
struct RoundCheckbox; struct RoundCheckbox;
} // namespace style } // namespace style
namespace Data {
class Media;
} // namespace Data
namespace Overview { namespace Overview {
namespace Layout { namespace Layout {
@ -172,7 +176,7 @@ private:
}; };
struct Info : public RuntimeComponent<Info> { struct Info : public RuntimeComponent<Info, LayoutItemBase> {
int top = 0; int top = 0;
}; };
@ -325,7 +329,7 @@ class Link : public ItemBase {
public: public:
Link( Link(
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,
HistoryMedia *media); Data::Media *media);
void initDimensions() override; void initDimensions() override;
int32 resizeGetHeight(int32 width) override; int32 resizeGetHeight(int32 width) override;

View file

@ -2698,24 +2698,35 @@ bool Text::hasSkipBlock() const {
return _blocks.empty() ? false : _blocks.back()->type() == TextBlockTSkip; return _blocks.empty() ? false : _blocks.back()->type() == TextBlockTSkip;
} }
void Text::setSkipBlock(int32 width, int32 height) { bool Text::updateSkipBlock(int width, int height) {
if (!_blocks.empty() && _blocks.back()->type() == TextBlockTSkip) { if (!_blocks.empty() && _blocks.back()->type() == TextBlockTSkip) {
auto block = static_cast<SkipBlock*>(_blocks.back().get()); const auto block = static_cast<SkipBlock*>(_blocks.back().get());
if (block->width() == width && block->height() == height) return; if (block->width() == width && block->height() == height) {
return false;
}
_text.resize(block->from()); _text.resize(block->from());
_blocks.pop_back(); _blocks.pop_back();
} }
_text.push_back('_'); _text.push_back('_');
_blocks.push_back(std::make_unique<SkipBlock>(_st->font, _text, _text.size() - 1, width, height, 0)); _blocks.push_back(std::make_unique<SkipBlock>(
_st->font,
_text,
_text.size() - 1,
width,
height,
0));
recountNaturalSize(false); recountNaturalSize(false);
return true;
} }
void Text::removeSkipBlock() { bool Text::removeSkipBlock() {
if (!_blocks.empty() && _blocks.back()->type() == TextBlockTSkip) { if (_blocks.empty() || _blocks.back()->type() != TextBlockTSkip) {
return false;
}
_text.resize(_blocks.back()->from()); _text.resize(_blocks.back()->from());
_blocks.pop_back(); _blocks.pop_back();
recountNaturalSize(false); recountNaturalSize(false);
} return true;
} }
int Text::countWidth(int width) const { int Text::countWidth(int width) const {

View file

@ -89,8 +89,8 @@ public:
bool hasLinks() const; bool hasLinks() const;
bool hasSkipBlock() const; bool hasSkipBlock() const;
void setSkipBlock(int32 width, int32 height); bool updateSkipBlock(int width, int height);
void removeSkipBlock(); bool removeSkipBlock();
int32 maxWidth() const { int32 maxWidth() const {
return _maxWidth.ceil().toInt(); return _maxWidth.ceil().toInt();

View file

@ -427,13 +427,24 @@ void Manager::notificationReplied(
} }
void NativeManager::doShowNotification(HistoryItem *item, int forwardedCount) { void NativeManager::doShowNotification(HistoryItem *item, int forwardedCount) {
auto options = getNotificationOptions(item); const auto options = getNotificationOptions(item);
QString title = options.hideNameAndPhoto ? qsl("Telegram Desktop") : item->history()->peer->name; const auto title = options.hideNameAndPhoto ? qsl("Telegram Desktop") : item->history()->peer->name;
QString subtitle = options.hideNameAndPhoto ? QString() : item->notificationHeader(); const auto subtitle = options.hideNameAndPhoto ? QString() : item->notificationHeader();
QString text = options.hideMessageText ? lang(lng_notification_preview) : (forwardedCount < 2 ? item->notificationText() : lng_forward_messages(lt_count, forwardedCount)); const auto text = options.hideMessageText
? lang(lng_notification_preview)
: (forwardedCount < 2
? item->notificationText()
: lng_forward_messages(lt_count, forwardedCount));
doShowNativeNotification(item->history()->peer, item->id, title, subtitle, text, options.hideNameAndPhoto, options.hideReplyButton); doShowNativeNotification(
item->history()->peer,
item->id,
title,
subtitle,
text,
options.hideNameAndPhoto,
options.hideReplyButton);
} }
System::~System() = default; System::~System() = default;

View file

@ -173,6 +173,10 @@
<(src_loc)/data/data_feed_messages.h <(src_loc)/data/data_feed_messages.h
<(src_loc)/data/data_flags.h <(src_loc)/data/data_flags.h
<(src_loc)/data/data_game.h <(src_loc)/data/data_game.h
<(src_loc)/data/data_groups.cpp
<(src_loc)/data/data_groups.h
<(src_loc)/data/data_media_types.cpp
<(src_loc)/data/data_media_types.h
<(src_loc)/data/data_messages.cpp <(src_loc)/data/data_messages.cpp
<(src_loc)/data/data_messages.h <(src_loc)/data/data_messages.h
<(src_loc)/data/data_notify_settings.cpp <(src_loc)/data/data_notify_settings.cpp