mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 02:01:40 -05:00
Use HistoryMedia as view, add Data::Media.
This commit is contained in:
parent
97a9089ebf
commit
7425e80f05
66 changed files with 4463 additions and 3130 deletions
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_photo.h"
|
||||
#include "data/data_web_page.h"
|
||||
#include "data/data_feed.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "core/tl_help.h"
|
||||
#include "base/overload.h"
|
||||
#include "observer_peer.h"
|
||||
|
@ -2936,13 +2937,13 @@ void ApiWrap::sendUploadedPhoto(
|
|||
const MTPInputFile &file,
|
||||
bool silent) {
|
||||
if (const auto item = App::histItemById(localId)) {
|
||||
const auto caption = item->getMedia()
|
||||
? item->getMedia()->getCaption()
|
||||
: TextWithEntities();
|
||||
const auto caption = item->media()
|
||||
? item->media()->caption()
|
||||
: QString();
|
||||
const auto media = MTP_inputMediaUploadedPhoto(
|
||||
MTP_flags(0),
|
||||
file,
|
||||
MTP_string(caption.text),
|
||||
MTP_string(caption),
|
||||
MTPVector<MTPInputDocument>(),
|
||||
MTP_int(0));
|
||||
if (const auto groupId = item->groupId()) {
|
||||
|
@ -2959,9 +2960,9 @@ void ApiWrap::sendUploadedDocument(
|
|||
const base::optional<MTPInputFile> &thumb,
|
||||
bool silent) {
|
||||
if (const auto item = App::histItemById(localId)) {
|
||||
auto media = item->getMedia();
|
||||
if (auto document = media ? media->getDocument() : nullptr) {
|
||||
const auto caption = media->getCaption();
|
||||
auto media = item->media();
|
||||
if (auto document = media ? media->document() : nullptr) {
|
||||
const auto caption = media->caption();
|
||||
const auto groupId = item->groupId();
|
||||
const auto flags = MTPDinputMediaUploadedDocument::Flags(0)
|
||||
| (thumb
|
||||
|
@ -2976,7 +2977,7 @@ void ApiWrap::sendUploadedDocument(
|
|||
thumb ? *thumb : MTPInputFile(),
|
||||
MTP_string(document->mimeString()),
|
||||
ComposeSendingDocumentAttributes(document),
|
||||
MTP_string(caption.text),
|
||||
MTP_string(caption),
|
||||
MTPVector<MTPInputDocument>(),
|
||||
MTP_int(0));
|
||||
if (groupId) {
|
||||
|
@ -3012,10 +3013,10 @@ void ApiWrap::uploadAlbumMedia(
|
|||
if (!item) {
|
||||
failed();
|
||||
}
|
||||
if (const auto media = item->getMedia()) {
|
||||
if (const auto photo = media->getPhoto()) {
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto photo = media->photo()) {
|
||||
photo->setWaitingForAlbum();
|
||||
} else if (const auto document = media->getDocument()) {
|
||||
} else if (const auto document = media->document()) {
|
||||
document->setWaitingForAlbum();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "styles/style_boxes.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "data/data_abstract_structure.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_location_manager.h"
|
||||
|
@ -978,10 +979,10 @@ namespace {
|
|||
|
||||
void checkSavedGif(HistoryItem *item) {
|
||||
if (!item->Has<HistoryMessageForwarded>() && (item->out() || item->history()->peer == App::self())) {
|
||||
if (auto media = item->getMedia()) {
|
||||
if (auto doc = media->getDocument()) {
|
||||
if (doc->isGifv()) {
|
||||
addSavedGif(doc);
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto document = media->document()) {
|
||||
if (document->isGifv()) {
|
||||
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) {
|
||||
auto data = fetchMsgsData(item->channelId(), false);
|
||||
if (!data) return;
|
||||
|
@ -2474,11 +2457,12 @@ namespace {
|
|||
if (!::gifItems.isEmpty()) {
|
||||
auto gifs = ::gifItems;
|
||||
for_const (auto item, gifs) {
|
||||
if (auto media = item->getMedia()) {
|
||||
if (!media->isRoundVideoPlaying()) {
|
||||
media->stopInline();
|
||||
}
|
||||
}
|
||||
// #TODO GIFs
|
||||
//if (auto media = item->getMedia()) {
|
||||
// if (!media->isRoundVideoPlaying()) {
|
||||
// media->stopInline();
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2486,9 +2470,9 @@ namespace {
|
|||
QString phoneFromSharedContact(int32 userId) {
|
||||
auto i = ::sharedContactItems.constFind(userId);
|
||||
if (i != ::sharedContactItems.cend() && !i->empty()) {
|
||||
if (auto media = (*i->cbegin())->getMedia()) {
|
||||
if (media->type() == MediaTypeContact) {
|
||||
return static_cast<HistoryContact*>(media)->phone();
|
||||
if (const auto media = (*i->cbegin())->media()) {
|
||||
if (const auto contact = media->sharedContact()) {
|
||||
return contact->phoneNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,7 +237,6 @@ namespace App {
|
|||
void historyClearItems();
|
||||
void historyRegDependency(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 historyUnregRandom(uint64 randomId);
|
||||
|
|
|
@ -259,4 +259,6 @@ private:
|
|||
const std::unique_ptr<Window::Notifications::System> _notifications;
|
||||
const std::unique_ptr<Core::Changelogs> _changelogs;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -31,7 +31,7 @@ const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask) {
|
|||
return i.value();
|
||||
}
|
||||
|
||||
const RuntimeComposerMetadata *RuntimeComposer::ZeroRuntimeComposerMetadata = GetRuntimeComposerMetadata(0);
|
||||
const RuntimeComposerMetadata *RuntimeComposerBase::ZeroRuntimeComposerMetadata = GetRuntimeComposerMetadata(0);
|
||||
|
||||
RuntimeComponentWrapStruct RuntimeComponentWraps[64];
|
||||
|
||||
|
|
|
@ -7,8 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
template <typename Base>
|
||||
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(*RuntimeComponentMove)(void *location, void *waslocation);
|
||||
|
||||
|
@ -38,8 +41,10 @@ struct CeilDivideMinimumOne {
|
|||
extern RuntimeComponentWrapStruct RuntimeComponentWraps[64];
|
||||
extern QAtomicInt RuntimeComponentIndexLast;
|
||||
|
||||
template <typename Type>
|
||||
template <typename Type, typename Base>
|
||||
struct RuntimeComponent {
|
||||
using RuntimeComponentBase = Base;
|
||||
|
||||
RuntimeComponent() {
|
||||
// While there is no std::aligned_alloc().
|
||||
static_assert(alignof(Type) <= alignof(std::max_align_t), "Components should align to std::max_align_t!");
|
||||
|
@ -76,7 +81,7 @@ struct RuntimeComponent {
|
|||
}
|
||||
|
||||
protected:
|
||||
static void RuntimeComponentConstruct(void *location, RuntimeComposer *composer) {
|
||||
static void RuntimeComponentConstruct(void *location, RuntimeComposerBase *composer) {
|
||||
new (location) Type();
|
||||
}
|
||||
static void RuntimeComponentDestruct(void *location) {
|
||||
|
@ -134,9 +139,9 @@ private:
|
|||
|
||||
const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask);
|
||||
|
||||
class RuntimeComposer {
|
||||
class RuntimeComposerBase {
|
||||
public:
|
||||
RuntimeComposer(uint64 mask = 0) : _data(zerodata()) {
|
||||
RuntimeComposerBase(uint64 mask = 0) : _data(zerodata()) {
|
||||
if (mask) {
|
||||
auto meta = GetRuntimeComposerMetadata(mask);
|
||||
|
||||
|
@ -169,9 +174,9 @@ public:
|
|||
}
|
||||
}
|
||||
}
|
||||
RuntimeComposer(const RuntimeComposer &other) = delete;
|
||||
RuntimeComposer &operator=(const RuntimeComposer &other) = delete;
|
||||
~RuntimeComposer() {
|
||||
RuntimeComposerBase(const RuntimeComposerBase &other) = delete;
|
||||
RuntimeComposerBase &operator=(const RuntimeComposerBase &other) = delete;
|
||||
~RuntimeComposerBase() {
|
||||
if (_data != zerodata()) {
|
||||
auto meta = _meta();
|
||||
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:
|
||||
void UpdateComponents(uint64 mask = 0) {
|
||||
if (!_meta()->equals(mask)) {
|
||||
RuntimeComposer tmp(mask);
|
||||
RuntimeComposerBase tmp(mask);
|
||||
tmp.swap(*this);
|
||||
if (_data != zerodata() && tmp._data != zerodata()) {
|
||||
auto meta = _meta(), wasmeta = tmp._meta();
|
||||
|
@ -223,6 +214,9 @@ protected:
|
|||
}
|
||||
|
||||
private:
|
||||
template <typename Base>
|
||||
friend class RuntimeComposer;
|
||||
|
||||
static const RuntimeComposerMetadata *ZeroRuntimeComposerMetadata;
|
||||
static void *zerodata() {
|
||||
return &ZeroRuntimeComposerMetadata;
|
||||
|
@ -239,8 +233,41 @@ private:
|
|||
}
|
||||
void *_data = nullptr;
|
||||
|
||||
void swap(RuntimeComposer &other) {
|
||||
void swap(RuntimeComposerBase &other) {
|
||||
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()]));
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -12,7 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "media/media_clip_reader.h"
|
||||
#include "history/history.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 "window/window_controller.h"
|
||||
#include "mainwidget.h"
|
||||
|
@ -22,59 +24,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
EditCaptionBox::EditCaptionBox(
|
||||
QWidget*,
|
||||
not_null<HistoryMedia*> media,
|
||||
not_null<Data::Media*> media,
|
||||
FullMsgId msgId)
|
||||
: _msgId(msgId) {
|
||||
Expects(media->canEditCaption());
|
||||
Expects(media->allowsEditCaption());
|
||||
|
||||
QSize dimensions;
|
||||
ImagePtr image;
|
||||
QString caption;
|
||||
DocumentData *doc = nullptr;
|
||||
|
||||
switch (media->type()) {
|
||||
case MediaTypeGif: {
|
||||
_animated = true;
|
||||
doc = static_cast<HistoryGif*>(media.get())->getDocument();
|
||||
dimensions = doc->dimensions;
|
||||
image = doc->thumb;
|
||||
} break;
|
||||
|
||||
case MediaTypePhoto: {
|
||||
if (const auto photo = media->photo()) {
|
||||
_photo = true;
|
||||
auto photo = static_cast<HistoryPhoto*>(media.get())->getPhoto();
|
||||
dimensions = QSize(photo->full->width(), photo->full->height());
|
||||
image = photo->full;
|
||||
} break;
|
||||
|
||||
case MediaTypeVideo: {
|
||||
_animated = true;
|
||||
doc = static_cast<HistoryVideo*>(media.get())->getDocument();
|
||||
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;
|
||||
} else if (const auto document = media->document()) {
|
||||
dimensions = document->dimensions;
|
||||
image = document->thumb;
|
||||
if (document->isAnimation()) {
|
||||
_animated = true;
|
||||
} else if (document->isVideoFile()) {
|
||||
_animated = true;
|
||||
} else {
|
||||
_doc = true;
|
||||
}
|
||||
} break;
|
||||
|
||||
case MediaTypeFile:
|
||||
case MediaTypeMusicFile:
|
||||
case MediaTypeVoiceFile: {
|
||||
_doc = true;
|
||||
doc = static_cast<HistoryDocument*>(media.get())->getDocument();
|
||||
image = doc->thumb;
|
||||
} break;
|
||||
doc = document;
|
||||
}
|
||||
caption = media->getCaption().text;
|
||||
auto caption = media->caption();
|
||||
|
||||
if (!_animated && (dimensions.isEmpty() || doc || image->isNull())) {
|
||||
if (image->isNull()) {
|
||||
|
|
|
@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
class HistoryMedia;
|
||||
namespace Data {
|
||||
class Media;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
class InputArea;
|
||||
|
@ -17,7 +19,7 @@ class InputArea;
|
|||
|
||||
class EditCaptionBox : public BoxContent, public RPCSender {
|
||||
public:
|
||||
EditCaptionBox(QWidget*, not_null<HistoryMedia*> media, FullMsgId msgId);
|
||||
EditCaptionBox(QWidget*, not_null<Data::Media*> media, FullMsgId msgId);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
|
|
@ -15,10 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "calls/calls_instance.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "mainwidget.h"
|
||||
#include "auth_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
|
||||
namespace Calls {
|
||||
namespace {
|
||||
|
@ -178,10 +178,11 @@ BoxController::Row::Type BoxController::Row::ComputeType(
|
|||
not_null<const HistoryItem*> item) {
|
||||
if (item->out()) {
|
||||
return Type::Out;
|
||||
} else if (auto media = item->getMedia()) {
|
||||
if (media->type() == MediaTypeCall) {
|
||||
auto reason = static_cast<HistoryCall*>(media)->reason();
|
||||
if (reason == HistoryCall::FinishReason::Busy || reason == HistoryCall::FinishReason::Missed) {
|
||||
} else if (auto media = item->media()) {
|
||||
if (const auto call = media->call()) {
|
||||
const auto reason = call->finishReason;
|
||||
if (reason == Data::Call::FinishReason::Busy
|
||||
|| reason == Data::Call::FinishReason::Missed) {
|
||||
return Type::Missed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -285,15 +285,15 @@ void DocumentOpenClickHandler::doOpen(
|
|||
if (App::main()) App::main()->mediaMarkRead(data);
|
||||
} else if (data->size < App::kImageSizeLimit) {
|
||||
if (!data->data().isEmpty() && playAnimation) {
|
||||
if (action == ActionOnLoadPlayInline && context && context->getMedia()) {
|
||||
context->getMedia()->playInline();
|
||||
if (action == ActionOnLoadPlayInline && context) {
|
||||
Auth().data().requestItemPlayInline(context);
|
||||
} else {
|
||||
Messenger::Instance().showDocument(data, context);
|
||||
}
|
||||
} else if (location.accessEnable()) {
|
||||
if (data->isAnimation() || QImageReader(location.name()).canRead()) {
|
||||
if (action == ActionOnLoadPlayInline && context && context->getMedia()) {
|
||||
context->getMedia()->playInline();
|
||||
if (action == ActionOnLoadPlayInline && context) {
|
||||
Auth().data().requestItemPlayInline(context);
|
||||
} else {
|
||||
Messenger::Instance().showDocument(data, context);
|
||||
}
|
||||
|
@ -548,7 +548,10 @@ void DocumentData::performActionOnLoad() {
|
|||
auto showImage = !isVideoFile() && (size < App::kImageSizeLimit);
|
||||
auto playVoice = isVoiceMessage() && (_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 (!loc.isEmpty() && loc.accessEnable()) {
|
||||
Messenger::Instance().showDocument(this, item);
|
||||
|
@ -588,8 +591,8 @@ void DocumentData::performActionOnLoad() {
|
|||
}
|
||||
} else if (playAnimation) {
|
||||
if (loaded()) {
|
||||
if (_actionOnLoad == ActionOnLoadPlayInline && item->getMedia()) {
|
||||
item->getMedia()->playInline();
|
||||
if (_actionOnLoad == ActionOnLoadPlayInline && item) {
|
||||
Auth().data().requestItemPlayInline(item);
|
||||
} else {
|
||||
Messenger::Instance().showDocument(this, item);
|
||||
}
|
||||
|
@ -607,8 +610,8 @@ void DocumentData::performActionOnLoad() {
|
|||
if (App::main()) App::main()->mediaMarkRead(this);
|
||||
} else if (loc.accessEnable()) {
|
||||
if (showImage && QImageReader(loc.name()).canRead()) {
|
||||
if (_actionOnLoad == ActionOnLoadPlayInline && item && item->getMedia()) {
|
||||
item->getMedia()->playInline();
|
||||
if (_actionOnLoad == ActionOnLoadPlayInline && item) {
|
||||
Auth().data().requestItemPlayInline(item);
|
||||
} else {
|
||||
Messenger::Instance().showDocument(this, item);
|
||||
}
|
||||
|
@ -764,7 +767,7 @@ void DocumentData::cancel() {
|
|||
void DocumentData::notifyLayoutChanged() const {
|
||||
auto &items = App::documentItems();
|
||||
for (auto item : items.value(const_cast<DocumentData*>(this))) {
|
||||
Auth().data().markItemLayoutChanged(item);
|
||||
Auth().data().markItemLayoutChange(item);
|
||||
}
|
||||
|
||||
if (auto items = InlineBots::Layout::documentItems()) {
|
||||
|
|
49
Telegram/SourceFiles/data/data_groups.cpp
Normal file
49
Telegram/SourceFiles/data/data_groups.cpp
Normal 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
|
32
Telegram/SourceFiles/data/data_groups.h
Normal file
32
Telegram/SourceFiles/data/data_groups.h
Normal 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
|
932
Telegram/SourceFiles/data/data_media_types.cpp
Normal file
932
Telegram/SourceFiles/data/data_media_types.cpp
Normal 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
|
347
Telegram/SourceFiles/data/data_media_types.h
Normal file
347
Telegram/SourceFiles/data/data_media_types.h
Normal 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
|
|
@ -66,7 +66,7 @@ void PhotoData::notifyLayoutChanged() const {
|
|||
auto i = items.constFind(const_cast<PhotoData*>(this));
|
||||
if (i != items.cend()) {
|
||||
for_const (auto item, i.value()) {
|
||||
Auth().data().markItemLayoutChanged(item);
|
||||
Auth().data().markItemLayoutChange(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "observer_peer.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "data/data_feed.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
using ViewElement = HistoryView::Element;
|
||||
|
||||
Session::Session() {
|
||||
Notify::PeerUpdateViewer(
|
||||
Notify::PeerUpdate::Flag::UserIsContact
|
||||
|
@ -27,12 +30,61 @@ Session::Session() {
|
|||
|
||||
Session::~Session() = default;
|
||||
|
||||
void Session::markItemLayoutChanged(not_null<const HistoryItem*> item) {
|
||||
_itemLayoutChanged.fire_copy(item);
|
||||
void Session::registerItemView(not_null<ViewElement*> view) {
|
||||
_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 {
|
||||
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) {
|
||||
|
@ -43,6 +95,14 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const
|
|||
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) {
|
||||
_itemViewResizeRequest.fire_copy(item);
|
||||
}
|
||||
|
@ -51,6 +111,14 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemViewResizeRequest() con
|
|||
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) {
|
||||
_itemViewRefreshRequest.fire_copy(item);
|
||||
}
|
||||
|
@ -59,6 +127,14 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemViewRefreshRequest() co
|
|||
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) {
|
||||
_itemRemoved.fire_copy(item);
|
||||
}
|
||||
|
@ -173,16 +249,9 @@ MessageIdsList Session::itemsToIds(
|
|||
}) | 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 {
|
||||
if (const auto group = item->getFullGroup()) {
|
||||
return groupToIds(group);
|
||||
if (const auto group = groups().find(item)) {
|
||||
return itemsToIds(group->items);
|
||||
}
|
||||
return { 1, item->fullId() };
|
||||
}
|
||||
|
|
|
@ -9,8 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "chat_helpers/stickers.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 {
|
||||
|
||||
|
@ -18,6 +24,8 @@ class Feed;
|
|||
|
||||
class Session final {
|
||||
public:
|
||||
using ViewElement = HistoryView::Element;
|
||||
|
||||
Session();
|
||||
~Session();
|
||||
|
||||
|
@ -30,6 +38,7 @@ public:
|
|||
base::Observable<void> &moreChatsLoaded() {
|
||||
return _moreChatsLoaded;
|
||||
}
|
||||
|
||||
base::Observable<void> &pendingHistoryResize() {
|
||||
return _pendingHistoryResize;
|
||||
}
|
||||
|
@ -40,18 +49,45 @@ public:
|
|||
base::Observable<ItemVisibilityQuery> &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;
|
||||
void markViewLayoutChange(not_null<const ViewElement*> view);
|
||||
rpl::producer<not_null<const ViewElement*>> viewLayoutChanged() const;
|
||||
void requestItemRepaint(not_null<const HistoryItem*> item);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
rpl::producer<not_null<const HistoryItem*>> itemRemoved() const;
|
||||
void markHistoryUnloaded(not_null<const History*> history);
|
||||
rpl::producer<not_null<const History*>> historyUnloaded() const;
|
||||
|
||||
void markHistoryCleared(not_null<const History*> history);
|
||||
rpl::producer<not_null<const History*>> historyCleared() const;
|
||||
using MegagroupParticipant = std::tuple<
|
||||
|
@ -147,7 +183,6 @@ public:
|
|||
|
||||
HistoryItemsList idsToItems(const MessageIdsList &ids) const;
|
||||
MessageIdsList itemsToIds(const HistoryItemsList &items) const;
|
||||
MessageIdsList groupToIds(not_null<HistoryMessageGroup*> group) const;
|
||||
MessageIdsList itemOrItsGroup(not_null<HistoryItem*> item) const;
|
||||
|
||||
int pinnedDialogsCount() const;
|
||||
|
@ -165,6 +200,13 @@ public:
|
|||
void setMimeForwardIds(MessageIdsList &&list);
|
||||
MessageIdsList takeMimeForwardIds();
|
||||
|
||||
Groups &groups() {
|
||||
return _groups;
|
||||
}
|
||||
const Groups &groups() const {
|
||||
return _groups;
|
||||
}
|
||||
|
||||
private:
|
||||
bool stickersUpdateNeeded(TimeMs lastUpdate, TimeMs now) const {
|
||||
constexpr auto kStickersUpdateTimeout = TimeMs(3600'000);
|
||||
|
@ -181,10 +223,15 @@ private:
|
|||
base::Observable<void> _moreChatsLoaded;
|
||||
base::Observable<void> _pendingHistoryResize;
|
||||
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 ViewElement*>> _viewRepaintRequest;
|
||||
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*>> _itemPlayInlineRequest;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
|
||||
rpl::event_stream<not_null<const History*>> _historyUnloaded;
|
||||
rpl::event_stream<not_null<const History*>> _historyCleared;
|
||||
|
@ -207,6 +254,10 @@ private:
|
|||
|
||||
std::deque<Dialogs::Key> _pinnedDialogs;
|
||||
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;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_sparse_ids.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "info/info_controller.h"
|
||||
|
@ -322,10 +323,8 @@ base::optional<bool> SharedMediaWithLastSlice::IsLastIsolated(
|
|||
}
|
||||
return LastFullMsgId(ending ? *ending : slice)
|
||||
| [](FullMsgId msgId) { return App::histItemById(msgId); }
|
||||
| [](HistoryItem *item) { return item ? item->getMedia() : nullptr; }
|
||||
| [](HistoryMedia *media) {
|
||||
return media ? media->getPhoto() : nullptr;
|
||||
}
|
||||
| [](HistoryItem *item) { return item ? item->media() : nullptr; }
|
||||
| [](Data::Media *media) { return media ? media->photo() : nullptr; }
|
||||
| [](PhotoData *photo) { return photo ? photo->id : 0; }
|
||||
| [&](PhotoId photoId) { return *lastPeerPhotoId != photoId; };
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/value_ordering.h"
|
||||
|
||||
class HistoryItem;
|
||||
using HistoryItemsList = std::vector<not_null<HistoryItem*>>;
|
||||
|
||||
|
@ -22,6 +24,32 @@ struct UploadState {
|
|||
|
||||
} // 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 UserData;
|
||||
class ChatData;
|
||||
|
|
|
@ -243,9 +243,10 @@ void autoplayMediaInlineAsync(const FullMsgId &msgId) {
|
|||
if (auto main = App::main()) {
|
||||
InvokeQueued(main, [msgId] {
|
||||
if (auto item = App::histItemById(msgId)) {
|
||||
if (auto media = item->getMedia()) {
|
||||
media->playInline(true);
|
||||
}
|
||||
// #TODO GIFs
|
||||
//if (auto media = item->getMedia()) {
|
||||
// media->playInline(true);
|
||||
//}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "lang/lang_keys.h"
|
||||
#include "boxes/edit_participant_box.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
|
||||
namespace AdminLog {
|
||||
namespace {
|
||||
|
@ -775,7 +776,7 @@ void InnerWidget::paintEmpty(Painter &p) {
|
|||
|
||||
TextWithEntities InnerWidget::getSelectedText() const {
|
||||
return _selectedItem
|
||||
? _selectedItem->data()->selectedText(_selectedText)
|
||||
? _selectedItem->selectedText(_selectedText)
|
||||
: TextWithEntities();
|
||||
}
|
||||
|
||||
|
@ -911,7 +912,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
const auto item = view ? view->data().get() : nullptr;
|
||||
const auto itemId = item ? item->fullId() : FullMsgId();
|
||||
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);
|
||||
if (isUponSelected > 0) {
|
||||
|
@ -919,7 +920,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
} else {
|
||||
if (item && !isUponSelected) {
|
||||
auto mediaHasTextForCopy = false;
|
||||
if (auto media = (msg ? msg->getMedia() : nullptr)) {
|
||||
if (auto media = view->media()) {
|
||||
mediaHasTextForCopy = media->hasTextForCopy();
|
||||
if (media->type() == MediaTypeWebPage && 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) {
|
||||
if (const auto item = App::histItemById(itemId)) {
|
||||
if (auto media = item->getMedia()) {
|
||||
if (auto document = media->getDocument()) {
|
||||
if (auto media = item->media()) {
|
||||
if (auto document = media->document()) {
|
||||
Messenger::Instance().showDocument(document, item);
|
||||
}
|
||||
}
|
||||
|
@ -1050,12 +1051,9 @@ void InnerWidget::openContextGif(FullMsgId itemId) {
|
|||
|
||||
void InnerWidget::copyContextText(FullMsgId itemId) {
|
||||
if (const auto item = App::histItemById(itemId)) {
|
||||
if (const auto media = item->getMedia()) {
|
||||
if (media->type() == MediaTypeSticker) {
|
||||
return;
|
||||
}
|
||||
if (const auto view = viewForItem(item)) {
|
||||
setToClipboard(view->selectedText(FullSelection));
|
||||
}
|
||||
setToClipboard(item->selectedText(FullSelection));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1416,7 +1414,7 @@ void InnerWidget::updateSelected() {
|
|||
}
|
||||
auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) };
|
||||
if (_mouseSelectType != TextSelectType::Letters) {
|
||||
selection = _mouseActionItem->data()->adjustSelection(
|
||||
selection = _mouseActionItem->adjustSelection(
|
||||
selection,
|
||||
_mouseSelectType);
|
||||
}
|
||||
|
@ -1517,17 +1515,21 @@ void InnerWidget::performDrag() {
|
|||
// auto forwardMimeType = QString();
|
||||
// auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
|
||||
// if (auto pressedItem = App::pressedItem()) {
|
||||
// pressedMedia = pressedItem->getMedia();
|
||||
// if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) {
|
||||
// pressedMedia = pressedItem->media();
|
||||
// if (_mouseCursorState == HistoryInDateCursorState
|
||||
// || (pressedMedia && pressedMedia->dragItem())) {
|
||||
// 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 ((pressedMedia = pressedLnkItem->getMedia())) {
|
||||
// if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) {
|
||||
// if ((pressedMedia = pressedLnkItem->media())) {
|
||||
// if (forwardMimeType.isEmpty()
|
||||
// && pressedMedia->dragItemByHandler(pressedHandler)) {
|
||||
// forwardMimeType = qsl("application/x-td-forward");
|
||||
// Auth().data().setMimeForwardIds({ 1, pressedLnkItem->fullId() });
|
||||
// Auth().data().setMimeForwardIds(
|
||||
// { 1, pressedLnkItem->fullId() });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -422,9 +422,13 @@ void GenerateItems(
|
|||
auto text = lng_admin_log_pinned_message(lt_from, fromLinkText);
|
||||
addSimpleServiceMessage(text);
|
||||
|
||||
auto applyServiceAction = 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);
|
||||
|
||||
auto oldValue = ExtractEditedText(action.vprev_message);
|
||||
auto applyServiceAction = 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()) {
|
||||
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);
|
||||
addSimpleServiceMessage(text);
|
||||
|
||||
auto applyServiceAction = 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 = [&]() {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
|
||||
// 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 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*> 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*> newItem,
|
||||
not_null<HistoryItem*> item,
|
||||
int blockIndex,
|
||||
int itemIndex);
|
||||
|
||||
|
@ -429,6 +433,13 @@ private:
|
|||
void changedInChatListHook(Dialogs::Mode list, bool added) 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.
|
||||
void checkLastMsg();
|
||||
|
||||
|
@ -441,17 +452,19 @@ private:
|
|||
|
||||
void clearSendAction(not_null<UserData*> from);
|
||||
|
||||
HistoryItem *findPreviousItem(not_null<HistoryItem*> item) const;
|
||||
HistoryItem *findNextItem(not_null<HistoryItem*> item) const;
|
||||
not_null<HistoryItem*> findGroupFirst(
|
||||
not_null<HistoryItem*> item) const;
|
||||
not_null<HistoryItem*> findGroupLast(
|
||||
not_null<HistoryItem*> item) const;
|
||||
auto recountGroupingFromTill(not_null<HistoryItem*> item)
|
||||
-> std::pair<not_null<HistoryItem*>, not_null<HistoryItem*>>;
|
||||
HistoryView::Element *findPreviousItem(
|
||||
not_null<HistoryView::Element*> view) const;
|
||||
HistoryView::Element *findNextItem(
|
||||
not_null<HistoryView::Element*> view) const;
|
||||
not_null<HistoryView::Element*> findGroupFirst(
|
||||
not_null<HistoryView::Element*> view) const;
|
||||
not_null<HistoryView::Element*> findGroupLast(
|
||||
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(
|
||||
not_null<HistoryItem*> from,
|
||||
not_null<HistoryItem*> till);
|
||||
not_null<HistoryView::Element*> from,
|
||||
not_null<HistoryView::Element*> till);
|
||||
|
||||
enum class Flag {
|
||||
f_has_pending_resized_items = (1 << 0),
|
||||
|
|
|
@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "apiwrap.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -405,7 +406,7 @@ void HistoryInner::enumerateDates(Method method) {
|
|||
|
||||
TextSelection HistoryInner::computeRenderSelection(
|
||||
not_null<const SelectedItems*> selected,
|
||||
not_null<HistoryItem*> item) const {
|
||||
not_null<Element*> view) const {
|
||||
const auto itemSelection = [&](not_null<HistoryItem*> item) {
|
||||
auto i = selected->find(item);
|
||||
if (i != selected->end()) {
|
||||
|
@ -413,22 +414,22 @@ TextSelection HistoryInner::computeRenderSelection(
|
|||
}
|
||||
return TextSelection();
|
||||
};
|
||||
const auto group = item->Get<HistoryMessageGroup>();
|
||||
const auto group = view->Get<HistoryView::Group>();
|
||||
if (group) {
|
||||
if (group->leader != item) {
|
||||
if (group->leader != view) {
|
||||
return TextSelection();
|
||||
}
|
||||
auto result = TextSelection();
|
||||
auto allFullSelected = true;
|
||||
const auto count = int(group->others.size());
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
if (itemSelection(group->others[i]) == FullSelection) {
|
||||
if (itemSelection(group->others[i]->data()) == FullSelection) {
|
||||
result = AddGroupItemSelection(result, i);
|
||||
} else {
|
||||
allFullSelected = false;
|
||||
}
|
||||
}
|
||||
const auto leaderSelection = itemSelection(item);
|
||||
const auto leaderSelection = itemSelection(view->data());
|
||||
if (leaderSelection == FullSelection) {
|
||||
return allFullSelected
|
||||
? FullSelection
|
||||
|
@ -438,7 +439,7 @@ TextSelection HistoryInner::computeRenderSelection(
|
|||
}
|
||||
return result;
|
||||
}
|
||||
return itemSelection(item);
|
||||
return itemSelection(view->data());
|
||||
}
|
||||
|
||||
TextSelection HistoryInner::itemRenderSelection(
|
||||
|
@ -452,7 +453,7 @@ TextSelection HistoryInner::itemRenderSelection(
|
|||
return FullSelection;
|
||||
}
|
||||
} else if (!_selected.empty()) {
|
||||
return computeRenderSelection(&_selected, item);
|
||||
return computeRenderSelection(&_selected, view);
|
||||
}
|
||||
return TextSelection();
|
||||
}
|
||||
|
@ -528,7 +529,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
}
|
||||
if (item->mentionsMe() && item->isMediaUnread()) {
|
||||
readMentions.insert(item);
|
||||
_widget->enqueueMessageHighlight(item);
|
||||
_widget->enqueueMessageHighlight(view);
|
||||
}
|
||||
|
||||
int32 h = view->height();
|
||||
|
@ -574,7 +575,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
}
|
||||
if (item->mentionsMe() && item->isMediaUnread()) {
|
||||
readMentions.insert(item);
|
||||
_widget->enqueueMessageHighlight(item);
|
||||
_widget->enqueueMessageHighlight(view);
|
||||
}
|
||||
}
|
||||
p.translate(0, h);
|
||||
|
@ -984,7 +985,7 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but
|
|||
if (uponSelected) {
|
||||
_mouseAction = MouseAction::PrepareDrag; // start text drag
|
||||
} 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
|
||||
} else {
|
||||
if (dragState.afterSymbol) ++_mouseTextSymbol;
|
||||
|
@ -1089,17 +1090,23 @@ void HistoryInner::performDrag() {
|
|||
auto forwardMimeType = QString();
|
||||
auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
|
||||
if (auto pressedItem = App::pressedItem()) {
|
||||
pressedMedia = pressedItem->data()->getMedia();
|
||||
if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) {
|
||||
Auth().data().setMimeForwardIds(Auth().data().itemOrItsGroup(pressedItem->data()));
|
||||
pressedMedia = pressedItem->media();
|
||||
if (_mouseCursorState == HistoryInDateCursorState
|
||||
|| (pressedMedia && pressedMedia->dragItem())) {
|
||||
Auth().data().setMimeForwardIds(
|
||||
Auth().data().itemOrItsGroup(pressedItem->data()));
|
||||
forwardMimeType = qsl("application/x-td-forward");
|
||||
}
|
||||
}
|
||||
if (const auto pressedLnkItem = _mouseActionItem) {
|
||||
if ((pressedMedia = pressedLnkItem->getMedia())) {
|
||||
if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) {
|
||||
Auth().data().setMimeForwardIds({ 1, pressedLnkItem->fullId() });
|
||||
forwardMimeType = qsl("application/x-td-forward");
|
||||
if (const auto view = pressedLnkItem->mainView()) {
|
||||
if ((pressedMedia = view->media())) {
|
||||
if (forwardMimeType.isEmpty()
|
||||
&& pressedMedia->dragItemByHandler(pressedHandler)) {
|
||||
Auth().data().setMimeForwardIds(
|
||||
{ 1, pressedLnkItem->fullId() });
|
||||
forwardMimeType = qsl("application/x-td-forward");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1162,9 +1169,11 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu
|
|||
// if we are in selecting items mode perhaps we want to
|
||||
// toggle selection instead of activating the pressed link
|
||||
if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) {
|
||||
if (auto media = _mouseActionItem->getMedia()) {
|
||||
if (media->toggleSelectionByHandlerClick(activated)) {
|
||||
activated = nullptr;
|
||||
if (const auto view = _mouseActionItem->mainView()) {
|
||||
if (const auto media = view->media()) {
|
||||
if (media->toggleSelectionByHandlerClick(activated)) {
|
||||
activated = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1232,7 +1241,11 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu
|
|||
#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64
|
||||
if (!_selected.empty() && _selected.cbegin()->second != FullSelection) {
|
||||
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
|
||||
}
|
||||
|
@ -1340,7 +1353,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
_widget->replyToMessage(itemId);
|
||||
});
|
||||
}
|
||||
if (item->canEdit(::date(unixtime()))) {
|
||||
if (item->allowsEdit(::date(unixtime()))) {
|
||||
_menu->addAction(lang(lng_context_edit_msg), [=] {
|
||||
_widget->editMessage(itemId);
|
||||
});
|
||||
|
@ -1423,7 +1436,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
} else if (item) {
|
||||
const auto itemId = item->fullId();
|
||||
if (isUponSelected != -2) {
|
||||
if (item->canForward()) {
|
||||
if (item->allowsForward()) {
|
||||
_menu->addAction(lang(lng_context_forward_msg), [=] {
|
||||
forwardItem(itemId);
|
||||
})->setEnabled(true);
|
||||
|
@ -1453,10 +1466,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
: App::hoveredLinkItem()
|
||||
? App::hoveredLinkItem()->data().get()
|
||||
: nullptr) {
|
||||
if (const auto group = result->getFullGroup()) {
|
||||
return group->others.empty()
|
||||
? group->leader
|
||||
: group->others.front().get();
|
||||
if (const auto group = Auth().data().groups().find(result)) {
|
||||
return group->items.front();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -1466,8 +1477,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
const auto canDelete = item
|
||||
&& item->canDelete()
|
||||
&& (item->id > 0 || !item->serviceMsg());
|
||||
const auto canForward = item
|
||||
&& item->canForward();
|
||||
const auto canForward = item && item->allowsForward();
|
||||
const auto view = item ? item->mainView() : nullptr;
|
||||
|
||||
const auto msg = dynamic_cast<HistoryMessage*>(item);
|
||||
if (isUponSelected > 0) {
|
||||
|
@ -1477,7 +1488,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
addItemActions(item);
|
||||
if (item && !isUponSelected) {
|
||||
auto mediaHasTextForCopy = false;
|
||||
if (auto media = (msg ? msg->getMedia() : nullptr)) {
|
||||
if (auto media = (view ? view->media() : nullptr)) {
|
||||
mediaHasTextForCopy = media->hasTextForCopy();
|
||||
if (media->type() == MediaTypeWebPage && 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) {
|
||||
if (const auto item = App::histItemById(itemId)) {
|
||||
if (const auto media = item->getMedia()) {
|
||||
if (const auto document = media->getDocument()) {
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto document = media->document()) {
|
||||
Messenger::Instance().showDocument(document, item);
|
||||
}
|
||||
}
|
||||
|
@ -1675,8 +1686,8 @@ void HistoryInner::openContextGif(FullMsgId itemId) {
|
|||
|
||||
void HistoryInner::saveContextGif(FullMsgId itemId) {
|
||||
if (const auto item = App::histItemById(itemId)) {
|
||||
if (const auto media = item->getMedia()) {
|
||||
if (const auto document = media->getDocument()) {
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto document = media->document()) {
|
||||
_widget->saveGif(document);
|
||||
}
|
||||
}
|
||||
|
@ -1685,14 +1696,11 @@ void HistoryInner::saveContextGif(FullMsgId itemId) {
|
|||
|
||||
void HistoryInner::copyContextText(FullMsgId itemId) {
|
||||
if (const auto item = App::histItemById(itemId)) {
|
||||
if (const auto media = item->getMedia()) {
|
||||
if (media->type() == MediaTypeSticker) {
|
||||
return;
|
||||
}
|
||||
if (const auto view = item->mainView()) {
|
||||
const auto group = view->getFullGroup();
|
||||
const auto leader = group ? group->leader : view;
|
||||
setToClipboard(leader->selectedText(FullSelection));
|
||||
}
|
||||
const auto group = item->getFullGroup();
|
||||
const auto leader = group ? group->leader : item;
|
||||
setToClipboard(leader->selectedText(FullSelection));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1718,26 +1726,30 @@ TextWithEntities HistoryInner::getSelectedText() const {
|
|||
}
|
||||
if (selected.cbegin()->second != FullSelection) {
|
||||
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");
|
||||
auto groupLeadersAdded = base::flat_set<not_null<HistoryItem*>>();
|
||||
auto groupLeadersAdded = base::flat_set<not_null<Element*>>();
|
||||
auto fullSize = 0;
|
||||
auto texts = base::flat_map<std::pair<int, MsgId>, TextWithEntities>();
|
||||
|
||||
const auto addItem = [&](
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<HistoryView::Element*> view,
|
||||
TextSelection selection) {
|
||||
const auto item = view->data();
|
||||
auto time = item->date.toString(timeFormat);
|
||||
auto part = TextWithEntities();
|
||||
auto unwrapped = item->selectedText(selection);
|
||||
auto unwrapped = view->selectedText(selection);
|
||||
auto size = item->author()->name.size()
|
||||
+ time.size()
|
||||
+ unwrapped.text.size();
|
||||
part.text.reserve(size);
|
||||
|
||||
auto y = itemTop(item);
|
||||
auto y = itemTop(view);
|
||||
if (y >= 0) {
|
||||
part.text.append(item->author()->name).append(time);
|
||||
TextUtilities::Append(part, std::move(unwrapped));
|
||||
|
@ -1747,11 +1759,12 @@ TextWithEntities HistoryInner::getSelectedText() const {
|
|||
};
|
||||
|
||||
for (const auto [item, selection] : selected) {
|
||||
if (!item->mainView()) {
|
||||
const auto view = item->mainView();
|
||||
if (!view) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto group = item->Get<HistoryMessageGroup>()) {
|
||||
if (const auto group = view->getFullGroup()) {
|
||||
if (groupLeadersAdded.contains(group->leader)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1761,16 +1774,16 @@ TextWithEntities HistoryInner::getSelectedText() const {
|
|||
if (leaderSelection == FullSelection) {
|
||||
groupLeadersAdded.emplace(group->leader);
|
||||
addItem(group->leader, FullSelection);
|
||||
} else if (item == group->leader) {
|
||||
} else if (view == group->leader) {
|
||||
const auto leaderFullSelection = AddGroupItemSelection(
|
||||
TextSelection(),
|
||||
int(group->others.size()));
|
||||
addItem(item, leaderFullSelection);
|
||||
addItem(view, leaderFullSelection);
|
||||
} else {
|
||||
addItem(item, FullSelection);
|
||||
addItem(view, FullSelection);
|
||||
}
|
||||
} else {
|
||||
addItem(item, FullSelection);
|
||||
addItem(view, FullSelection);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2174,7 +2187,7 @@ auto HistoryInner::getSelectionState() const
|
|||
if (selected.first->canDelete()) {
|
||||
++result.canDeleteCount;
|
||||
}
|
||||
if (selected.first->canForward()) {
|
||||
if (selected.first->allowsForward()) {
|
||||
++result.canForwardCount;
|
||||
}
|
||||
} else {
|
||||
|
@ -2411,7 +2424,9 @@ void HistoryInner::onUpdateSelected() {
|
|||
}
|
||||
auto selState = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) };
|
||||
if (_mouseSelectType != TextSelectType::Letters) {
|
||||
selState = _mouseActionItem->adjustSelection(selState, _mouseSelectType);
|
||||
if (const auto view = _mouseActionItem->mainView()) {
|
||||
selState = view->adjustSelection(selState, _mouseSelectType);
|
||||
}
|
||||
}
|
||||
if (_selected[_mouseActionItem] != selState) {
|
||||
_selected[_mouseActionItem] = selState;
|
||||
|
@ -2570,7 +2585,7 @@ int HistoryInner::itemTop(const HistoryItem *item) const {
|
|||
}
|
||||
|
||||
int HistoryInner::itemTop(const Element *view) const {
|
||||
if (!view) {
|
||||
if (!view || view->data()->mainView() != view) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -2609,10 +2624,12 @@ int HistoryInner::moveScrollFollowingInlineKeyboard(
|
|||
const HistoryItem *item,
|
||||
int oldKeyboardTop,
|
||||
int newKeyboardTop) {
|
||||
if (item->isUnderCursor()) {
|
||||
const auto top = itemTop(item);
|
||||
if (top >= oldKeyboardTop) {
|
||||
return newKeyboardTop - oldKeyboardTop;
|
||||
if (const auto view = item ? item->mainView() : nullptr) {
|
||||
if (view->isUnderCursor()) {
|
||||
const auto top = itemTop(item);
|
||||
if (top >= oldKeyboardTop) {
|
||||
return newKeyboardTop - oldKeyboardTop;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -2632,11 +2649,8 @@ bool HistoryInner::isSelected(
|
|||
bool HistoryInner::isSelectedAsGroup(
|
||||
not_null<SelectedItems*> toItems,
|
||||
not_null<HistoryItem*> item) const {
|
||||
if (const auto group = item->getFullGroup()) {
|
||||
if (!isSelected(toItems, group->leader)) {
|
||||
return false;
|
||||
}
|
||||
for (const auto other : group->others) {
|
||||
if (const auto group = Auth().data().groups().find(item)) {
|
||||
for (const auto other : group->items) {
|
||||
if (!isSelected(toItems, other)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -2703,7 +2717,7 @@ void HistoryInner::changeSelectionAsGroup(
|
|||
not_null<SelectedItems*> toItems,
|
||||
not_null<HistoryItem*> item,
|
||||
SelectAction action) const {
|
||||
const auto group = item->getFullGroup();
|
||||
const auto group = Auth().data().groups().find(item);
|
||||
if (!group) {
|
||||
return changeSelection(toItems, item, action);
|
||||
}
|
||||
|
@ -2716,10 +2730,7 @@ void HistoryInner::changeSelectionAsGroup(
|
|||
const auto add = (action == SelectAction::Select);
|
||||
|
||||
const auto adding = [&] {
|
||||
if (!add || !goodForSelection(toItems, group->leader, total)) {
|
||||
return false;
|
||||
}
|
||||
for (const auto other : group->others) {
|
||||
for (const auto other : group->items) {
|
||||
if (!goodForSelection(toItems, other, total)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -2727,13 +2738,11 @@ void HistoryInner::changeSelectionAsGroup(
|
|||
return (total <= MaxSelectedItems);
|
||||
}();
|
||||
if (adding) {
|
||||
addToSelection(toItems, group->leader);
|
||||
for (const auto other : group->others) {
|
||||
for (const auto other : group->items) {
|
||||
addToSelection(toItems, other);
|
||||
}
|
||||
} else {
|
||||
removeFromSelection(toItems, group->leader);
|
||||
for (const auto other : group->others) {
|
||||
for (const auto other : group->items) {
|
||||
removeFromSelection(toItems, other);
|
||||
}
|
||||
}
|
||||
|
@ -2745,13 +2754,7 @@ void HistoryInner::forwardItem(FullMsgId itemId) {
|
|||
|
||||
void HistoryInner::forwardAsGroup(FullMsgId itemId) {
|
||||
if (const auto item = App::histItemById(itemId)) {
|
||||
if (const auto group = item->getFullGroup()) {
|
||||
auto items = Auth().data().itemsToIds(group->others);
|
||||
items.push_back(group->leader->fullId());
|
||||
Window::ShowForwardMessagesBox(std::move(items));
|
||||
} else {
|
||||
Window::ShowForwardMessagesBox({ 1, itemId });
|
||||
}
|
||||
Window::ShowForwardMessagesBox(Auth().data().itemOrItsGroup(item));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2779,13 +2782,12 @@ bool HistoryInner::hasPendingResizedItems() const {
|
|||
|
||||
void HistoryInner::deleteAsGroup(FullMsgId itemId) {
|
||||
if (const auto item = App::histItemById(itemId)) {
|
||||
const auto group = item->getFullGroup();
|
||||
if (!group || group->others.empty()) {
|
||||
const auto group = Auth().data().groups().find(item);
|
||||
if (!group || group->items.size() < 2) {
|
||||
return deleteItem(item);
|
||||
}
|
||||
auto items = Auth().data().itemsToIds(group->others);
|
||||
items.push_back(group->leader->fullId());
|
||||
Ui::show(Box<DeleteMessagesBox>(Auth().data().groupToIds(group)));
|
||||
Ui::show(Box<DeleteMessagesBox>(
|
||||
Auth().data().itemsToIds(group->items)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2866,7 +2868,7 @@ QString HistoryInner::tooltipText() const {
|
|||
if (const auto view = App::hoveredItem()) {
|
||||
auto dateText = view->data()->date.toString(
|
||||
QLocale::system().dateTimeFormat(QLocale::LongFormat));
|
||||
auto editedDate = view->data()->displayedEditDate();
|
||||
auto editedDate = view->displayedEditDate();
|
||||
if (!editedDate.isNull()) {
|
||||
dateText += '\n' + lng_edited_date(lt_date, editedDate.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)));
|
||||
}
|
||||
|
|
|
@ -212,7 +212,7 @@ private:
|
|||
int seltoy) const;
|
||||
TextSelection computeRenderSelection(
|
||||
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);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history_item_components.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "history/history_media_grouped.h"
|
||||
#include "history/history_service.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history.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 "data/data_session.h"
|
||||
#include "data/data_messages.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_feed.h"
|
||||
|
||||
namespace internal {
|
||||
namespace {
|
||||
|
||||
TextSelection unshiftSelection(TextSelection selection, uint16 byLength) {
|
||||
if (selection == FullSelection) {
|
||||
return selection;
|
||||
}
|
||||
return ::unshiftSelection(selection, byLength);
|
||||
not_null<HistoryItem*> CreateUnsupportedMessage(
|
||||
not_null<History*> history,
|
||||
MsgId msgId,
|
||||
MTPDmessage::Flags flags,
|
||||
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) {
|
||||
if (selection == FullSelection) {
|
||||
return selection;
|
||||
}
|
||||
return ::shiftSelection(selection, byLength);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace
|
||||
|
||||
HistoryItem::HistoryItem(
|
||||
not_null<History*> history,
|
||||
|
@ -93,6 +109,12 @@ void HistoryItem::finishEdition(int oldKeyboardTop) {
|
|||
App::historyUpdateDependent(this);
|
||||
}
|
||||
|
||||
void HistoryItem::setGroupId(MessageGroupId groupId) {
|
||||
Auth().data().groups().unregisterMessage(this);
|
||||
_groupId = groupId;
|
||||
Auth().data().groups().registerMessage(this);
|
||||
}
|
||||
|
||||
HistoryMessageReplyMarkup *HistoryItem::inlineReplyMarkup() {
|
||||
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
|
||||
if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_inline) {
|
||||
|
@ -189,12 +211,17 @@ MTPDreplyKeyboardMarkup::Flags HistoryItem::replyKeyboardFlags() const {
|
|||
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());
|
||||
|
||||
AddComponents(HistoryMessageLogEntryOriginal::Bit());
|
||||
auto original = Get<HistoryMessageLogEntryOriginal>();
|
||||
auto webpage = App::feedWebPage(localId, label, content);
|
||||
original->_page = std::make_unique<HistoryWebPage>(this, webpage);
|
||||
Get<HistoryMessageLogEntryOriginal>()->page = App::feedWebPage(
|
||||
localId,
|
||||
label,
|
||||
content);
|
||||
}
|
||||
|
||||
UserData *HistoryItem::viaBot() const {
|
||||
|
@ -276,20 +303,11 @@ void HistoryItem::removeMainView() {
|
|||
|
||||
void HistoryItem::clearMainView() {
|
||||
_mainView = nullptr;
|
||||
|
||||
validateGroupId();
|
||||
if (groupId()) {
|
||||
makeGroupLeader({});
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryItem::addToUnreadMentions(UnreadMentionType type) {
|
||||
}
|
||||
|
||||
Storage::SharedMediaTypesMask HistoryItem::sharedMediaTypes() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
void HistoryItem::indexAsNewItem() {
|
||||
if (IsServerMsgId(id)) {
|
||||
CrashReports::SetAnnotation("addToUnreadMentions", QString::number(id));
|
||||
|
@ -307,27 +325,21 @@ void HistoryItem::indexAsNewItem() {
|
|||
void HistoryItem::setRealId(MsgId newId) {
|
||||
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
|
||||
// in history widget, because it can't exist for an outgoing message.
|
||||
// Only inline keyboards can be in outgoing messages.
|
||||
if (auto markup = inlineReplyMarkup()) {
|
||||
if (const auto markup = inlineReplyMarkup()) {
|
||||
if (markup->inlineKeyboard) {
|
||||
markup->inlineKeyboard->updateMessageId();
|
||||
}
|
||||
}
|
||||
|
||||
if (_media) {
|
||||
_media->refreshParentId(this);
|
||||
if (const auto group = Get<HistoryMessageGroup>()) {
|
||||
if (group->leader != this) {
|
||||
if (const auto media = group->leader->getMedia()) {
|
||||
media->refreshParentId(group->leader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Auth().data().markItemIdChange({ this, oldId });
|
||||
Auth().data().requestItemRepaint(this);
|
||||
}
|
||||
|
||||
bool HistoryItem::isPinned() const {
|
||||
|
@ -347,60 +359,11 @@ bool HistoryItem::canPin() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool HistoryItem::canForward() const {
|
||||
if (id < 0 || isLogEntry()) {
|
||||
return false;
|
||||
}
|
||||
if (auto message = toHistoryMessage()) {
|
||||
if (auto media = message->getMedia()) {
|
||||
if (media->type() == MediaTypeCall) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool HistoryItem::allowsForward() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HistoryItem::canEdit(const QDateTime &cur) 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();
|
||||
}
|
||||
}
|
||||
bool HistoryItem::allowsEdit(const QDateTime &now) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -443,8 +406,8 @@ bool HistoryItem::canDeleteForEveryone(const QDateTime &cur) const {
|
|||
if (!toHistoryMessage()) {
|
||||
return false;
|
||||
}
|
||||
if (auto media = getMedia()) {
|
||||
if (media->type() == MediaTypeCall) {
|
||||
if (const auto media = this->media()) {
|
||||
if (!media->allowsRevoke()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -493,8 +456,8 @@ QString HistoryItem::directLink() const {
|
|||
Assert(channel != nullptr);
|
||||
auto query = channel->username + '/' + QString::number(id);
|
||||
if (!channel->isMegagroup()) {
|
||||
if (auto media = getMedia()) {
|
||||
if (auto document = media->getDocument()) {
|
||||
if (const auto media = this->media()) {
|
||||
if (const auto document = media->document()) {
|
||||
if (document->isVideoMessage()) {
|
||||
return qsl("https://telesco.pe/") + query;
|
||||
}
|
||||
|
@ -565,13 +528,6 @@ MsgId HistoryItem::idOriginal() const {
|
|||
return id;
|
||||
}
|
||||
|
||||
bool HistoryItem::hasOutLayout() const {
|
||||
if (history()->peer->isSelf()) {
|
||||
return !Has<HistoryMessageForwarded>();
|
||||
}
|
||||
return out() && !isPost();
|
||||
}
|
||||
|
||||
bool HistoryItem::needCheck() const {
|
||||
return out() || (id < 0 && history()->peer->isSelf());
|
||||
}
|
||||
|
@ -658,84 +614,7 @@ void HistoryItem::setUnreadBarFreezed() {
|
|||
}
|
||||
|
||||
MessageGroupId HistoryItem::groupId() const {
|
||||
if (const auto group = Get<HistoryMessageGroup>()) {
|
||||
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);
|
||||
return _groupId;
|
||||
}
|
||||
|
||||
int HistoryItem::displayedDateHeight() const {
|
||||
|
@ -758,79 +637,74 @@ bool HistoryItem::isEmpty() const {
|
|||
void HistoryItem::clipCallback(Media::Clip::Notification notification) {
|
||||
using namespace Media::Clip;
|
||||
|
||||
auto media = getMedia();
|
||||
auto media = this->media();
|
||||
if (!media) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto reader = media->getClipReader();
|
||||
if (!reader) {
|
||||
return;
|
||||
}
|
||||
// #TODO GIFs
|
||||
//auto reader = media->getClipReader();
|
||||
//if (!reader) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
switch (notification) {
|
||||
case NotificationReinit: {
|
||||
auto stopped = false;
|
||||
if (reader->autoPausedGif()) {
|
||||
auto amVisible = false;
|
||||
Auth().data().queryItemVisibility().notify({ this, &amVisible }, true);
|
||||
if (!amVisible) { // stop animation if it is not visible
|
||||
media->stopInline();
|
||||
if (auto document = media->getDocument()) { // forget data from memory
|
||||
document->forget();
|
||||
}
|
||||
stopped = true;
|
||||
}
|
||||
} else if (reader->mode() == Media::Clip::Reader::Mode::Video && reader->state() == Media::Clip::State::Finished) {
|
||||
// Stop finished video message.
|
||||
media->stopInline();
|
||||
}
|
||||
if (!stopped) {
|
||||
Auth().data().requestItemViewResize(this);
|
||||
Auth().data().markItemLayoutChanged(this);
|
||||
Global::RefPendingRepaintItems().insert(this);
|
||||
}
|
||||
} break;
|
||||
//switch (notification) {
|
||||
//case NotificationReinit: {
|
||||
// auto stopped = false;
|
||||
// if (reader->autoPausedGif()) {
|
||||
// auto amVisible = false;
|
||||
// Auth().data().queryItemVisibility().notify({ this, &amVisible }, true);
|
||||
// if (!amVisible) { // stop animation if it is not visible
|
||||
// media->stopInline();
|
||||
// if (auto document = media->getDocument()) { // forget data from memory
|
||||
// document->forget();
|
||||
// }
|
||||
// stopped = true;
|
||||
// }
|
||||
// } else if (reader->mode() == Media::Clip::Reader::Mode::Video && reader->state() == Media::Clip::State::Finished) {
|
||||
// // Stop finished video message.
|
||||
// media->stopInline();
|
||||
// }
|
||||
// if (!stopped) {
|
||||
// Auth().data().requestItemViewResize(this);
|
||||
// Auth().data().markItemLayoutChange(this);
|
||||
// Global::RefPendingRepaintItems().insert(this);
|
||||
// }
|
||||
//} break;
|
||||
|
||||
case NotificationRepaint: {
|
||||
if (!reader->currentDisplayed()) {
|
||||
Auth().data().requestItemRepaint(this);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
//case NotificationRepaint: {
|
||||
// if (!reader->currentDisplayed()) {
|
||||
// Auth().data().requestItemRepaint(this);
|
||||
// }
|
||||
//} break;
|
||||
//}
|
||||
}
|
||||
|
||||
void HistoryItem::audioTrackUpdated() {
|
||||
auto media = getMedia();
|
||||
auto media = this->media();
|
||||
if (!media) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto reader = media->getClipReader();
|
||||
if (!reader || reader->mode() != Media::Clip::Reader::Mode::Video) {
|
||||
return;
|
||||
}
|
||||
// #TODO GIFs
|
||||
//auto reader = media->getClipReader();
|
||||
//if (!reader || reader->mode() != Media::Clip::Reader::Mode::Video) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
auto audio = reader->audioMsgId();
|
||||
auto current = Media::Player::mixer()->currentState(audio.type());
|
||||
if (current.id != audio || Media::Player::IsStoppedOrStopping(current.state)) {
|
||||
media->stopInline();
|
||||
} else if (Media::Player::IsPaused(current.state) || current.state == Media::Player::State::Pausing) {
|
||||
if (!reader->videoPaused()) {
|
||||
reader->pauseResumeVideo();
|
||||
}
|
||||
} else {
|
||||
if (reader->videoPaused()) {
|
||||
reader->pauseResumeVideo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryItem::isUnderCursor() const {
|
||||
if (const auto view = App::hoveredItem()) {
|
||||
return view->data() == this;
|
||||
}
|
||||
return false;
|
||||
//auto audio = reader->audioMsgId();
|
||||
//auto current = Media::Player::mixer()->currentState(audio.type());
|
||||
//if (current.id != audio || Media::Player::IsStoppedOrStopping(current.state)) {
|
||||
// media->stopInline();
|
||||
//} else if (Media::Player::IsPaused(current.state) || current.state == Media::Player::State::Pausing) {
|
||||
// if (!reader->videoPaused()) {
|
||||
// reader->pauseResumeVideo();
|
||||
// }
|
||||
//} else {
|
||||
// if (reader->videoPaused()) {
|
||||
// reader->pauseResumeVideo();
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
HistoryItem *HistoryItem::previousItem() const {
|
||||
|
@ -869,7 +743,7 @@ QString HistoryItem::notificationText() const {
|
|||
QString HistoryItem::inDialogsText(DrawInDialog way) const {
|
||||
auto getText = [this]() {
|
||||
if (emptyText()) {
|
||||
return _media ? _media->inDialogsText() : QString();
|
||||
return _media ? _media->chatsListText() : QString();
|
||||
}
|
||||
return TextUtilities::Clean(_text.originalText());
|
||||
};
|
||||
|
@ -879,7 +753,7 @@ QString HistoryItem::inDialogsText(DrawInDialog way) const {
|
|||
return nullptr;
|
||||
} else if (!_history->peer->isUser() || out()) {
|
||||
return author();
|
||||
} else if (_history->peer->isSelf() && !hasOutLayout()) {
|
||||
} else if (_history->peer->isSelf() && !Has<HistoryMessageForwarded>()) {
|
||||
return senderOriginal();
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -914,6 +788,7 @@ void HistoryItem::drawInDialog(
|
|||
}
|
||||
|
||||
HistoryItem::~HistoryItem() {
|
||||
Auth().data().groups().unregisterMessage(this);
|
||||
App::historyUnregItem(this);
|
||||
if (id < 0 && !App::quitting()) {
|
||||
Auth().uploader().cancel(fullId());
|
||||
|
@ -940,3 +815,135 @@ ClickHandlerPtr goToMessageClickHandler(
|
|||
ClickHandlerPtr goToMessageClickHandler(not_null<HistoryItem*> item) {
|
||||
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().");
|
||||
}
|
||||
|
|
|
@ -14,8 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_cursor_state.h"
|
||||
|
||||
enum class UnreadMentionType;
|
||||
struct MessageGroupId;
|
||||
struct HistoryMessageGroup;
|
||||
struct HistoryMessageReplyMarkup;
|
||||
class ReplyKeyboard;
|
||||
class HistoryMessage;
|
||||
|
@ -42,6 +40,7 @@ struct RippleAnimation;
|
|||
|
||||
namespace Data {
|
||||
struct MessagePosition;
|
||||
class Media;
|
||||
} // namespace Data
|
||||
|
||||
namespace Window {
|
||||
|
@ -52,21 +51,12 @@ namespace HistoryView {
|
|||
enum class Context : char;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace internal {
|
||||
|
||||
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 {
|
||||
class HistoryItem : public RuntimeComposer<HistoryItem> {
|
||||
public:
|
||||
static not_null<HistoryItem*> Create(
|
||||
not_null<History*> history,
|
||||
const MTPMessage &message);
|
||||
|
||||
virtual void dependencyItemRemoved(HistoryItem *dependency) {
|
||||
}
|
||||
virtual bool updateDependencyItem() {
|
||||
|
@ -145,19 +135,12 @@ public:
|
|||
bool isSilent() const {
|
||||
return _flags & MTPDmessage::Flag::f_silent;
|
||||
}
|
||||
bool hasOutLayout() const;
|
||||
virtual int32 viewsCount() const {
|
||||
virtual int viewsCount() const {
|
||||
return hasViews() ? 1 : -1;
|
||||
}
|
||||
|
||||
virtual bool needCheck() const;
|
||||
|
||||
[[nodiscard]] virtual TextSelection adjustSelection(
|
||||
TextSelection selection,
|
||||
TextSelectType type) const {
|
||||
return selection;
|
||||
}
|
||||
|
||||
virtual bool serviceMsg() const {
|
||||
return false;
|
||||
}
|
||||
|
@ -173,17 +156,10 @@ public:
|
|||
virtual void addToUnreadMentions(UnreadMentionType type);
|
||||
virtual void eraseFromUnreadMentions() {
|
||||
}
|
||||
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const;
|
||||
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const = 0;
|
||||
|
||||
void indexAsNewItem();
|
||||
|
||||
virtual bool hasBubble() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual TextWithEntities selectedText(TextSelection selection) const {
|
||||
return { qsl("[-]"), EntitiesInText() };
|
||||
}
|
||||
|
||||
virtual QString notificationHeader() const {
|
||||
return QString();
|
||||
}
|
||||
|
@ -204,29 +180,10 @@ public:
|
|||
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 setRealId(MsgId newId);
|
||||
|
||||
virtual bool displayEditedBadge() const {
|
||||
return false;
|
||||
}
|
||||
virtual QDateTime displayedEditDate() const {
|
||||
return QDateTime();
|
||||
}
|
||||
virtual void refreshEditedBadge() {
|
||||
}
|
||||
|
||||
void drawInDialog(
|
||||
Painter &p,
|
||||
const QRect &r,
|
||||
|
@ -242,8 +199,8 @@ public:
|
|||
|
||||
bool isPinned() const;
|
||||
bool canPin() const;
|
||||
bool canForward() const;
|
||||
bool canEdit(const QDateTime &cur) const;
|
||||
virtual bool allowsForward() const;
|
||||
virtual bool allowsEdit(const QDateTime &now) const;
|
||||
bool canDelete() const;
|
||||
bool canDeleteForEveryone(const QDateTime &cur) const;
|
||||
bool suggestBanReport() const;
|
||||
|
@ -261,7 +218,7 @@ public:
|
|||
}
|
||||
Data::MessagePosition position() const;
|
||||
|
||||
HistoryMedia *getMedia() const {
|
||||
Data::Media *media() const {
|
||||
return _media.get();
|
||||
}
|
||||
virtual void setText(const TextWithEntities &textWithEntities) {
|
||||
|
@ -270,29 +227,6 @@ public:
|
|||
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
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -328,25 +262,12 @@ public:
|
|||
}
|
||||
|
||||
bool isEmpty() const;
|
||||
bool isHiddenByGroup() const {
|
||||
return _flags & MTPDmessage_ClientFlag::f_hidden_by_group;
|
||||
}
|
||||
|
||||
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 audioTrackUpdated();
|
||||
|
||||
bool isUnderCursor() const;
|
||||
|
||||
HistoryItem *previousItem() const;
|
||||
HistoryItem *nextItem() const;
|
||||
|
||||
|
@ -388,27 +309,20 @@ protected:
|
|||
ReplyKeyboard *inlineReplyKeyboard();
|
||||
void invalidateChatsListEntry();
|
||||
|
||||
[[nodiscard]] TextSelection skipTextSelection(
|
||||
TextSelection selection) const {
|
||||
return internal::unshiftSelection(selection, _text);
|
||||
}
|
||||
[[nodiscard]] TextSelection unskipTextSelection(
|
||||
TextSelection selection) const {
|
||||
return internal::shiftSelection(selection, _text);
|
||||
}
|
||||
void setGroupId(MessageGroupId groupId);
|
||||
|
||||
Text _text = { int(st::msgMinWidth) };
|
||||
int _textWidth = -1;
|
||||
int _textHeight = 0;
|
||||
|
||||
HistoryMediaPtr _media;
|
||||
std::unique_ptr<Data::Media> _media;
|
||||
|
||||
private:
|
||||
void resetGroupMedia(const std::vector<not_null<HistoryItem*>> &others);
|
||||
|
||||
HistoryView::Element *_mainView = nullptr;
|
||||
friend class HistoryView::Element;
|
||||
|
||||
MessageGroupId _groupId = MessageGroupId::None;
|
||||
|
||||
};
|
||||
|
||||
// make all the constructors in HistoryItem children protected
|
||||
|
|
|
@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "media/media_audio.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "auth_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_session.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_history.h"
|
||||
|
@ -191,7 +192,7 @@ void HistoryMessageReply::updateName() const {
|
|||
: App::peerName(replyToMsg->author());
|
||||
replyToName.setText(st::fwdTextStyle, name, Ui::NameTextOptions());
|
||||
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 w = replyToName.maxWidth();
|
||||
if (replyToVia) {
|
||||
|
@ -207,7 +208,7 @@ void HistoryMessageReply::updateName() const {
|
|||
|
||||
void HistoryMessageReply::resize(int width) const {
|
||||
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;
|
||||
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();
|
||||
|
||||
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 (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()) {
|
||||
hasPreview = false;
|
||||
}
|
||||
auto previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
|
||||
|
||||
if (hasPreview) {
|
||||
ImagePtr replyPreview = replyToMsg->getMedia()->replyPreview();
|
||||
const auto replyPreview = replyToMsg->media()->replyPreview();
|
||||
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 previewWidth = replyPreview->width() / cIntRetinaFactor();
|
||||
|
@ -834,11 +841,14 @@ void HistoryMessageDate::paint(Painter &p, int y, int w) const {
|
|||
|
||||
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) {
|
||||
_page = std::move(other._page);
|
||||
HistoryMessageLogEntryOriginal &HistoryMessageLogEntryOriginal::operator=(
|
||||
HistoryMessageLogEntryOriginal &&other) {
|
||||
page = std::move(other.page);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,38 +8,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "history/history_item.h"
|
||||
#include "base/value_ordering.h"
|
||||
|
||||
class HistoryDocument;
|
||||
class HistoryWebPage;
|
||||
class WebPageData;
|
||||
|
||||
struct MessageGroupId {
|
||||
using Underlying = uint64;
|
||||
namespace HistoryView {
|
||||
class Element;
|
||||
} // namespace HistoryView
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct HistoryMessageVia : public RuntimeComponent<HistoryMessageVia> {
|
||||
struct HistoryMessageVia : public RuntimeComponent<HistoryMessageVia, HistoryItem> {
|
||||
void create(UserId userId);
|
||||
void resize(int32 availw) const;
|
||||
|
||||
|
@ -50,13 +27,13 @@ struct HistoryMessageVia : public RuntimeComponent<HistoryMessageVia> {
|
|||
ClickHandlerPtr link;
|
||||
};
|
||||
|
||||
struct HistoryMessageViews : public RuntimeComponent<HistoryMessageViews> {
|
||||
struct HistoryMessageViews : public RuntimeComponent<HistoryMessageViews, HistoryItem> {
|
||||
QString _viewsText;
|
||||
int _views = 0;
|
||||
int _viewsWidth = 0;
|
||||
};
|
||||
|
||||
struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned> {
|
||||
struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned, HistoryItem> {
|
||||
void refresh(const QString &date);
|
||||
int maxWidth() const;
|
||||
|
||||
|
@ -64,7 +41,7 @@ struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned> {
|
|||
Text signature;
|
||||
};
|
||||
|
||||
struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited> {
|
||||
struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited, HistoryItem> {
|
||||
void refresh(const QString &date, bool displayed);
|
||||
int maxWidth() const;
|
||||
|
||||
|
@ -72,7 +49,7 @@ struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited> {
|
|||
Text text;
|
||||
};
|
||||
|
||||
struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded> {
|
||||
struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded, HistoryItem> {
|
||||
void create(const HistoryMessageVia *via) const;
|
||||
|
||||
QDateTime originalDate;
|
||||
|
@ -85,7 +62,7 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded
|
|||
MsgId savedFromMsgId = 0;
|
||||
};
|
||||
|
||||
struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply> {
|
||||
struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply, HistoryItem> {
|
||||
HistoryMessageReply() = default;
|
||||
HistoryMessageReply(const 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; };
|
||||
void paint(
|
||||
Painter &p,
|
||||
const HistoryItem *holder,
|
||||
not_null<const HistoryView::Element*> holder,
|
||||
int x,
|
||||
int y,
|
||||
int w,
|
||||
|
@ -171,7 +148,7 @@ struct HistoryMessageMarkupButton {
|
|||
|
||||
};
|
||||
|
||||
struct HistoryMessageReplyMarkup : public RuntimeComponent<HistoryMessageReplyMarkup> {
|
||||
struct HistoryMessageReplyMarkup : public RuntimeComponent<HistoryMessageReplyMarkup, HistoryItem> {
|
||||
using Button = HistoryMessageMarkupButton;
|
||||
|
||||
HistoryMessageReplyMarkup() = default;
|
||||
|
@ -348,7 +325,7 @@ private:
|
|||
|
||||
// Any HistoryItem can have this Component for
|
||||
// displaying the day mark above the message.
|
||||
struct HistoryMessageDate : public RuntimeComponent<HistoryMessageDate> {
|
||||
struct HistoryMessageDate : public RuntimeComponent<HistoryMessageDate, HistoryItem> {
|
||||
void init(const QDateTime &date);
|
||||
|
||||
int height() const;
|
||||
|
@ -360,7 +337,7 @@ struct HistoryMessageDate : public RuntimeComponent<HistoryMessageDate> {
|
|||
|
||||
// Any HistoryItem can have this Component for
|
||||
// displaying the unread messages bar above the message.
|
||||
struct HistoryMessageUnreadBar : public RuntimeComponent<HistoryMessageUnreadBar> {
|
||||
struct HistoryMessageUnreadBar : public RuntimeComponent<HistoryMessageUnreadBar, HistoryItem> {
|
||||
void init(int count);
|
||||
|
||||
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.
|
||||
struct HistoryMessageLogEntryOriginal : public RuntimeComponent<HistoryMessageLogEntryOriginal> {
|
||||
struct HistoryMessageLogEntryOriginal
|
||||
: public RuntimeComponent<HistoryMessageLogEntryOriginal, HistoryItem> {
|
||||
HistoryMessageLogEntryOriginal();
|
||||
HistoryMessageLogEntryOriginal(HistoryMessageLogEntryOriginal &&other);
|
||||
HistoryMessageLogEntryOriginal &operator=(HistoryMessageLogEntryOriginal &&other);
|
||||
~HistoryMessageLogEntryOriginal();
|
||||
|
||||
std::unique_ptr<HistoryWebPage> _page;
|
||||
WebPageData *page = nullptr;
|
||||
|
||||
};
|
||||
|
||||
class FileClickHandler;
|
||||
struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed> {
|
||||
struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed, HistoryDocument> {
|
||||
std::shared_ptr<FileClickHandler> _linksavel, _linkcancell;
|
||||
int _thumbw = 0;
|
||||
|
||||
|
@ -407,13 +379,13 @@ struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed>
|
|||
mutable QString _link;
|
||||
};
|
||||
|
||||
struct HistoryDocumentCaptioned : public RuntimeComponent<HistoryDocumentCaptioned> {
|
||||
struct HistoryDocumentCaptioned : public RuntimeComponent<HistoryDocumentCaptioned, HistoryDocument> {
|
||||
HistoryDocumentCaptioned();
|
||||
|
||||
Text _caption;
|
||||
};
|
||||
|
||||
struct HistoryDocumentNamed : public RuntimeComponent<HistoryDocumentNamed> {
|
||||
struct HistoryDocumentNamed : public RuntimeComponent<HistoryDocumentNamed, HistoryDocument> {
|
||||
QString _name;
|
||||
int _namew = 0;
|
||||
};
|
||||
|
@ -426,7 +398,7 @@ struct HistoryDocumentVoicePlayback {
|
|||
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.
|
||||
static constexpr float64 kFloatToIntMultiplier = 65536.;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history_media.h"
|
||||
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
|
||||
Storage::SharedMediaTypesMask HistoryMedia::sharedMediaTypes() const {
|
||||
|
@ -23,11 +24,15 @@ QSize HistoryMedia::countCurrentSize(int newWidth) {
|
|||
}
|
||||
|
||||
TextSelection HistoryMedia::skipSelection(TextSelection selection) const {
|
||||
return internal::unshiftSelection(selection, fullSelectionLength());
|
||||
return HistoryView::UnshiftItemSelection(
|
||||
selection,
|
||||
fullSelectionLength());
|
||||
}
|
||||
|
||||
TextSelection HistoryMedia::unskipSelection(TextSelection selection) const {
|
||||
return internal::shiftSelection(selection, fullSelectionLength());
|
||||
return HistoryView::ShiftItemSelection(
|
||||
selection,
|
||||
fullSelectionLength());
|
||||
}
|
||||
|
||||
HistoryTextState HistoryMedia::getStateGrouped(
|
||||
|
|
|
@ -52,21 +52,18 @@ enum HistoryMediaType : char {
|
|||
|
||||
class HistoryMedia : public HistoryView::Object {
|
||||
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 QString notificationText() const {
|
||||
return QString();
|
||||
virtual std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<Element*> newParent,
|
||||
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;
|
||||
|
||||
bool hasPoint(QPoint point) const {
|
||||
|
@ -85,7 +82,7 @@ public:
|
|||
virtual bool allowsFastShare() const {
|
||||
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 HistoryTextState getState(QPoint point, HistoryStateRequest request) const = 0;
|
||||
|
@ -109,10 +106,6 @@ public:
|
|||
TextSelectType type) const {
|
||||
return selection;
|
||||
}
|
||||
[[nodiscard]] virtual bool consumeMessageText(
|
||||
const TextWithEntities &textWithEntities) {
|
||||
return false;
|
||||
}
|
||||
[[nodiscard]] virtual uint16 fullSelectionLength() const {
|
||||
return 0;
|
||||
}
|
||||
|
@ -132,9 +125,6 @@ public:
|
|||
virtual bool uploading() const {
|
||||
return false;
|
||||
}
|
||||
virtual std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const = 0;
|
||||
|
||||
virtual PhotoData *getPhoto() const {
|
||||
return nullptr;
|
||||
|
@ -187,20 +177,10 @@ public:
|
|||
virtual std::unique_ptr<HistoryMedia> takeLastFromGroup() {
|
||||
return nullptr;
|
||||
}
|
||||
virtual bool applyGroup(
|
||||
const std::vector<not_null<HistoryItem*>> &others) {
|
||||
virtual bool applyGroup(const std::vector<not_null<Element*>> &others) {
|
||||
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 {
|
||||
return false;
|
||||
}
|
||||
|
@ -253,10 +233,6 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
virtual bool canEditCaption() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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)
|
||||
// But the overloading click handler should be used only when media
|
||||
|
@ -271,7 +247,7 @@ public:
|
|||
protected:
|
||||
QSize countCurrentSize(int newWidth) override;
|
||||
|
||||
not_null<HistoryItem*> _parent;
|
||||
not_null<Element*> _parent;
|
||||
MediaInBubbleState _inBubbleState = MediaInBubbleState::None;
|
||||
|
||||
};
|
||||
|
|
|
@ -18,13 +18,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "styles/style_history.h"
|
||||
#include "layout.h"
|
||||
|
||||
HistoryGroupedMedia::Element::Element(not_null<HistoryItem*> item)
|
||||
: item(item) {
|
||||
HistoryGroupedMedia::Part::Part(not_null<Element*> view)
|
||||
: view(view) {
|
||||
}
|
||||
|
||||
HistoryGroupedMedia::HistoryGroupedMedia(
|
||||
not_null<HistoryItem*> parent,
|
||||
const std::vector<not_null<HistoryItem*>> &others)
|
||||
not_null<Element*> parent,
|
||||
const std::vector<not_null<Element*>> &others)
|
||||
: HistoryMedia(parent)
|
||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
||||
const auto result = applyGroup(others);
|
||||
|
@ -32,23 +32,17 @@ HistoryGroupedMedia::HistoryGroupedMedia(
|
|||
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() {
|
||||
if (_caption.hasSkipBlock()) {
|
||||
_caption.setSkipBlock(
|
||||
_caption.updateSkipBlock(
|
||||
_parent->skipBlockWidth(),
|
||||
_parent->skipBlockHeight());
|
||||
}
|
||||
|
||||
std::vector<QSize> sizes;
|
||||
sizes.reserve(_elements.size());
|
||||
for (const auto &element : _elements) {
|
||||
const auto &media = element.content;
|
||||
sizes.reserve(_parts.size());
|
||||
for (const auto &part : _parts) {
|
||||
const auto &media = part.content;
|
||||
media->initDimensions();
|
||||
sizes.push_back(media->sizeForGrouping());
|
||||
}
|
||||
|
@ -58,7 +52,7 @@ QSize HistoryGroupedMedia::countOptimalSize() {
|
|||
st::historyGroupWidthMax,
|
||||
st::historyGroupWidthMin,
|
||||
st::historyGroupSkip);
|
||||
Assert(layout.size() == _elements.size());
|
||||
Assert(layout.size() == _parts.size());
|
||||
|
||||
auto maxWidth = 0;
|
||||
auto minHeight = 0;
|
||||
|
@ -66,8 +60,8 @@ QSize HistoryGroupedMedia::countOptimalSize() {
|
|||
const auto &item = layout[i];
|
||||
accumulate_max(maxWidth, item.geometry.x() + item.geometry.width());
|
||||
accumulate_max(minHeight, item.geometry.y() + item.geometry.height());
|
||||
_elements[i].initialGeometry = item.geometry;
|
||||
_elements[i].sides = item.sides;
|
||||
_parts[i].initialGeometry = item.geometry;
|
||||
_parts[i].sides = item.sides;
|
||||
}
|
||||
|
||||
if (!_caption.isEmpty()) {
|
||||
|
@ -93,9 +87,9 @@ QSize HistoryGroupedMedia::countCurrentSize(int newWidth) {
|
|||
return int(std::round(value * factor));
|
||||
};
|
||||
const auto spacing = scale(initialSpacing);
|
||||
for (auto &element : _elements) {
|
||||
const auto sides = element.sides;
|
||||
const auto initialGeometry = element.initialGeometry;
|
||||
for (auto &part : _parts) {
|
||||
const auto sides = part.sides;
|
||||
const auto initialGeometry = part.initialGeometry;
|
||||
const auto needRightSkip = !(sides & RectPart::Right);
|
||||
const auto needBottomSkip = !(sides & RectPart::Bottom);
|
||||
const auto initialLeft = initialGeometry.x();
|
||||
|
@ -114,7 +108,7 @@ QSize HistoryGroupedMedia::countCurrentSize(int newWidth) {
|
|||
const auto height = scale(initialBottom)
|
||||
- top
|
||||
- (needBottomSkip ? spacing : 0);
|
||||
element.geometry = QRect(left, top, width, height);
|
||||
part.geometry = QRect(left, top, width, height);
|
||||
|
||||
accumulate_max(newHeight, top + height);
|
||||
}
|
||||
|
@ -130,10 +124,9 @@ QSize HistoryGroupedMedia::countCurrentSize(int newWidth) {
|
|||
return { newWidth, newHeight };
|
||||
}
|
||||
|
||||
void HistoryGroupedMedia::refreshParentId(
|
||||
not_null<HistoryItem*> realParent) {
|
||||
for (const auto &element : _elements) {
|
||||
element.content->refreshParentId(element.item);
|
||||
void HistoryGroupedMedia::refreshParentId(not_null<Element*> realParent) {
|
||||
for (const auto &part : _parts) {
|
||||
part.content->refreshParentId(part.view);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,29 +135,29 @@ void HistoryGroupedMedia::draw(
|
|||
const QRect &clip,
|
||||
TextSelection selection,
|
||||
TimeMs ms) const {
|
||||
for (auto i = 0, count = int(_elements.size()); i != count; ++i) {
|
||||
const auto &element = _elements[i];
|
||||
const auto elementSelection = (selection == FullSelection)
|
||||
for (auto i = 0, count = int(_parts.size()); i != count; ++i) {
|
||||
const auto &part = _parts[i];
|
||||
const auto partSelection = (selection == FullSelection)
|
||||
? FullSelection
|
||||
: IsGroupItemSelection(selection, i)
|
||||
? FullSelection
|
||||
: TextSelection();
|
||||
auto corners = Ui::GetCornersFromSides(element.sides);
|
||||
auto corners = Ui::GetCornersFromSides(part.sides);
|
||||
if (!isBubbleTop()) {
|
||||
corners &= ~(RectPart::TopLeft | RectPart::TopRight);
|
||||
}
|
||||
if (!isBubbleBottom() || !_caption.isEmpty()) {
|
||||
corners &= ~(RectPart::BottomLeft | RectPart::BottomRight);
|
||||
}
|
||||
element.content->drawGrouped(
|
||||
part.content->drawGrouped(
|
||||
p,
|
||||
clip,
|
||||
elementSelection,
|
||||
partSelection,
|
||||
ms,
|
||||
element.geometry,
|
||||
part.geometry,
|
||||
corners,
|
||||
&element.cacheKey,
|
||||
&element.cache);
|
||||
&part.cacheKey,
|
||||
&part.cache);
|
||||
}
|
||||
|
||||
// date
|
||||
|
@ -177,7 +170,7 @@ void HistoryGroupedMedia::draw(
|
|||
- _caption.countHeight(captionw);
|
||||
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);
|
||||
} else if (_parent->getMedia() == this) {
|
||||
} else if (_parent->media() == this) {
|
||||
auto fullRight = width();
|
||||
auto fullBottom = height();
|
||||
if (needInfoDisplay()) {
|
||||
|
@ -191,38 +184,38 @@ void HistoryGroupedMedia::draw(
|
|||
}
|
||||
}
|
||||
|
||||
HistoryTextState HistoryGroupedMedia::getElementState(
|
||||
HistoryTextState HistoryGroupedMedia::getPartState(
|
||||
QPoint point,
|
||||
HistoryStateRequest request) const {
|
||||
for (const auto &element : _elements) {
|
||||
if (element.geometry.contains(point)) {
|
||||
auto result = element.content->getStateGrouped(
|
||||
element.geometry,
|
||||
for (const auto &part : _parts) {
|
||||
if (part.geometry.contains(point)) {
|
||||
auto result = part.content->getStateGrouped(
|
||||
part.geometry,
|
||||
point,
|
||||
request);
|
||||
result.itemId = element.item->fullId();
|
||||
result.itemId = part.view->data()->fullId();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return HistoryTextState(_parent);
|
||||
return HistoryTextState(_parent->data());
|
||||
}
|
||||
|
||||
HistoryTextState HistoryGroupedMedia::getState(
|
||||
QPoint point,
|
||||
HistoryStateRequest request) const {
|
||||
auto result = getElementState(point, request);
|
||||
auto result = getPartState(point, request);
|
||||
if (!result.link && !_caption.isEmpty()) {
|
||||
const auto captionw = width() - st::msgPadding.left() - st::msgPadding.right();
|
||||
const auto captiony = height()
|
||||
- (isBubbleBottom() ? st::msgPadding.bottom() : 0)
|
||||
- _caption.countHeight(captionw);
|
||||
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),
|
||||
captionw,
|
||||
request.forText()));
|
||||
}
|
||||
} else if (_parent->getMedia() == this) {
|
||||
} else if (_parent->media() == this) {
|
||||
auto fullRight = width();
|
||||
auto fullBottom = height();
|
||||
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
|
||||
|
@ -241,8 +234,8 @@ HistoryTextState HistoryGroupedMedia::getState(
|
|||
|
||||
bool HistoryGroupedMedia::toggleSelectionByHandlerClick(
|
||||
const ClickHandlerPtr &p) const {
|
||||
for (const auto &element : _elements) {
|
||||
if (element.content->toggleSelectionByHandlerClick(p)) {
|
||||
for (const auto &part : _parts) {
|
||||
if (part.content->toggleSelectionByHandlerClick(p)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -250,8 +243,8 @@ bool HistoryGroupedMedia::toggleSelectionByHandlerClick(
|
|||
}
|
||||
|
||||
bool HistoryGroupedMedia::dragItemByHandler(const ClickHandlerPtr &p) const {
|
||||
for (const auto &element : _elements) {
|
||||
if (element.content->dragItemByHandler(p)) {
|
||||
for (const auto &part : _parts) {
|
||||
if (part.content->dragItemByHandler(p)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -264,14 +257,6 @@ TextSelection HistoryGroupedMedia::adjustSelection(
|
|||
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(
|
||||
TextSelection selection) const {
|
||||
if (!IsSubGroupSelection(selection)) {
|
||||
|
@ -279,7 +264,7 @@ TextWithEntities HistoryGroupedMedia::selectedText(
|
|||
lang(lng_in_dlg_album),
|
||||
_caption,
|
||||
selection);
|
||||
} else if (IsGroupItemSelection(selection, int(_elements.size()) - 1)) {
|
||||
} else if (IsGroupItemSelection(selection, int(_parts.size()) - 1)) {
|
||||
return main()->selectedText(FullSelection);
|
||||
}
|
||||
return TextWithEntities();
|
||||
|
@ -288,78 +273,77 @@ TextWithEntities HistoryGroupedMedia::selectedText(
|
|||
void HistoryGroupedMedia::clickHandlerActiveChanged(
|
||||
const ClickHandlerPtr &p,
|
||||
bool active) {
|
||||
for (const auto &element : _elements) {
|
||||
element.content->clickHandlerActiveChanged(p, active);
|
||||
for (const auto &part : _parts) {
|
||||
part.content->clickHandlerActiveChanged(p, active);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryGroupedMedia::clickHandlerPressedChanged(
|
||||
const ClickHandlerPtr &p,
|
||||
bool pressed) {
|
||||
for (const auto &element : _elements) {
|
||||
element.content->clickHandlerPressedChanged(p, pressed);
|
||||
if (pressed && element.content->dragItemByHandler(p)) {
|
||||
// #TODO pressedLinkItem
|
||||
//App::pressedLinkItem(element.item);
|
||||
for (const auto &part : _parts) {
|
||||
part.content->clickHandlerPressedChanged(p, pressed);
|
||||
if (pressed && part.content->dragItemByHandler(p)) {
|
||||
App::pressedLinkItem(part.view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryGroupedMedia::attachToParent() {
|
||||
for (const auto &element : _elements) {
|
||||
element.content->attachToParent();
|
||||
for (const auto &part : _parts) {
|
||||
part.content->attachToParent();
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryGroupedMedia::detachFromParent() {
|
||||
for (const auto &element : _elements) {
|
||||
if (element.content) {
|
||||
element.content->detachFromParent();
|
||||
for (const auto &part : _parts) {
|
||||
if (part.content) {
|
||||
part.content->detachFromParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<HistoryMedia> HistoryGroupedMedia::takeLastFromGroup() {
|
||||
return std::move(_elements.back().content);
|
||||
return std::move(_parts.back().content);
|
||||
}
|
||||
|
||||
bool HistoryGroupedMedia::applyGroup(
|
||||
const std::vector<not_null<HistoryItem*>> &others) {
|
||||
const std::vector<not_null<Element*>> &others) {
|
||||
if (others.empty()) {
|
||||
return false;
|
||||
}
|
||||
const auto pushElement = [&](not_null<HistoryItem*> item) {
|
||||
const auto media = item->getMedia();
|
||||
const auto pushElement = [&](not_null<Element*> view) {
|
||||
const auto media = view->media();
|
||||
Assert(media != nullptr && media->canBeGrouped());
|
||||
|
||||
_elements.push_back(Element(item));
|
||||
_elements.back().content = item->getMedia()->clone(_parent, item);
|
||||
_parts.push_back(Part(view));
|
||||
_parts.back().content = media->clone(_parent, view);
|
||||
};
|
||||
if (_elements.empty()) {
|
||||
if (_parts.empty()) {
|
||||
pushElement(_parent);
|
||||
} else if (validateGroupElements(others)) {
|
||||
} else if (validateGroupParts(others)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We're updating other elements, so we just need to preserve the main.
|
||||
auto mainElement = std::move(_elements.back());
|
||||
_elements.erase(_elements.begin(), _elements.end());
|
||||
_elements.reserve(others.size() + 1);
|
||||
for (const auto item : others) {
|
||||
pushElement(item);
|
||||
auto mainPart = std::move(_parts.back());
|
||||
_parts.erase(_parts.begin(), _parts.end());
|
||||
_parts.reserve(others.size() + 1);
|
||||
for (const auto view : others) {
|
||||
pushElement(view);
|
||||
}
|
||||
_elements.push_back(std::move(mainElement));
|
||||
_parts.push_back(std::move(mainPart));
|
||||
//_parent->setPendingInitDimensions(); // #TODO group view
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HistoryGroupedMedia::validateGroupElements(
|
||||
const std::vector<not_null<HistoryItem*>> &others) const {
|
||||
if (_elements.size() != others.size() + 1) {
|
||||
bool HistoryGroupedMedia::validateGroupParts(
|
||||
const std::vector<not_null<Element*>> &others) const {
|
||||
if (_parts.size() != others.size() + 1) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -367,9 +351,9 @@ bool HistoryGroupedMedia::validateGroupElements(
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -388,15 +372,6 @@ Storage::SharedMediaTypesMask HistoryGroupedMedia::sharedMediaTypes() const {
|
|||
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 {
|
||||
return main()->getPhoto();
|
||||
}
|
||||
|
@ -407,25 +382,25 @@ DocumentData *HistoryGroupedMedia::getDocument() const {
|
|||
|
||||
HistoryMessageEdited *HistoryGroupedMedia::displayedEditBadge() const {
|
||||
if (!_caption.isEmpty()) {
|
||||
return _elements.front().item->Get<HistoryMessageEdited>();
|
||||
return _parts.front().view->data()->Get<HistoryMessageEdited>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void HistoryGroupedMedia::updateNeedBubbleState() {
|
||||
const auto getItemCaption = [](const Element &element) {
|
||||
if (const auto media = element.item->getMedia()) {
|
||||
const auto getItemCaption = [](const Part &part) {
|
||||
if (const auto media = part.view->media()) {
|
||||
return media->getCaption();
|
||||
}
|
||||
return element.content->getCaption();
|
||||
return part.content->getCaption();
|
||||
};
|
||||
const auto captionText = [&] {
|
||||
auto result = getItemCaption(_elements.front());
|
||||
auto result = getItemCaption(_parts.front());
|
||||
if (result.text.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
for (auto i = 1, count = int(_elements.size()); i != count; ++i) {
|
||||
if (!getItemCaption(_elements[i]).text.isEmpty()) {
|
||||
for (auto i = 1, count = int(_parts.size()); i != count; ++i) {
|
||||
if (!getItemCaption(_parts[i]).text.isEmpty()) {
|
||||
return TextWithEntities();
|
||||
}
|
||||
}
|
||||
|
@ -434,7 +409,7 @@ void HistoryGroupedMedia::updateNeedBubbleState() {
|
|||
_caption.setText(
|
||||
st::messageTextStyle,
|
||||
captionText.text + _parent->skipBlock(),
|
||||
Ui::ItemTextNoMonoOptions(_parent));
|
||||
Ui::ItemTextNoMonoOptions(_parent->data()));
|
||||
_needBubble = computeNeedBubble();
|
||||
}
|
||||
|
||||
|
@ -442,19 +417,15 @@ bool HistoryGroupedMedia::needsBubble() const {
|
|||
return _needBubble;
|
||||
}
|
||||
|
||||
bool HistoryGroupedMedia::canEditCaption() const {
|
||||
return main()->canEditCaption();
|
||||
}
|
||||
|
||||
bool HistoryGroupedMedia::computeNeedBubble() const {
|
||||
if (!_caption.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
if (const auto message = _parent->toHistoryMessage()) {
|
||||
if (message->viaBot()
|
||||
|| message->Has<HistoryMessageReply>()
|
||||
|| message->displayForwardedFrom()
|
||||
// || message->displayFromName() // #TODO media views
|
||||
if (const auto item = _parent->data()) {
|
||||
if (item->viaBot()
|
||||
|| item->Has<HistoryMessageReply>()
|
||||
|| _parent->displayForwardedFrom()
|
||||
|| _parent->displayFromName()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
@ -463,5 +434,5 @@ bool HistoryGroupedMedia::computeNeedBubble() const {
|
|||
}
|
||||
|
||||
bool HistoryGroupedMedia::needInfoDisplay() const {
|
||||
return (_parent->id < 0 || _parent->isUnderCursor());
|
||||
return (_parent->data()->id < 0 || _parent->isUnderCursor());
|
||||
}
|
||||
|
|
|
@ -14,19 +14,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
class HistoryGroupedMedia : public HistoryMedia {
|
||||
public:
|
||||
HistoryGroupedMedia(
|
||||
not_null<HistoryItem*> parent,
|
||||
const std::vector<not_null<HistoryItem*>> &others);
|
||||
not_null<Element*> parent,
|
||||
const std::vector<not_null<Element*>> &others);
|
||||
|
||||
HistoryMediaType type() const override {
|
||||
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 updateSentMedia(const MTPMessageMedia &media) override;
|
||||
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
|
||||
void refreshParentId(not_null<Element*> realParent) override;
|
||||
|
||||
void draw(
|
||||
Painter &p,
|
||||
|
@ -54,8 +49,6 @@ public:
|
|||
PhotoData *getPhoto() const override;
|
||||
DocumentData *getDocument() const override;
|
||||
|
||||
QString notificationText() const override;
|
||||
QString inDialogsText() const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
void clickHandlerActiveChanged(
|
||||
|
@ -69,7 +62,7 @@ public:
|
|||
void detachFromParent() override;
|
||||
std::unique_ptr<HistoryMedia> takeLastFromGroup() override;
|
||||
bool applyGroup(
|
||||
const std::vector<not_null<HistoryItem*>> &others) override;
|
||||
const std::vector<not_null<Element*>> &others) override;
|
||||
|
||||
bool hasReplyPreview() const override;
|
||||
ImagePtr replyPreview() override;
|
||||
|
@ -93,16 +86,15 @@ public:
|
|||
bool customInfoLayout() const override {
|
||||
return _caption.isEmpty();
|
||||
}
|
||||
bool canEditCaption() const override;
|
||||
bool allowsFastShare() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Element {
|
||||
Element(not_null<HistoryItem*> item);
|
||||
struct Part {
|
||||
Part(not_null<Element*> view);
|
||||
|
||||
not_null<HistoryItem*> item;
|
||||
not_null<Element*> view;
|
||||
std::unique_ptr<HistoryMedia> content;
|
||||
|
||||
RectParts sides = RectPart::None;
|
||||
|
@ -119,14 +111,14 @@ private:
|
|||
bool needInfoDisplay() const;
|
||||
bool computeNeedBubble() const;
|
||||
not_null<HistoryMedia*> main() const;
|
||||
bool validateGroupElements(
|
||||
const std::vector<not_null<HistoryItem*>> &others) const;
|
||||
HistoryTextState getElementState(
|
||||
bool validateGroupParts(
|
||||
const std::vector<not_null<Element*>> &others) const;
|
||||
HistoryTextState getPartState(
|
||||
QPoint point,
|
||||
HistoryStateRequest request) const;
|
||||
|
||||
Text _caption;
|
||||
std::vector<Element> _elements;
|
||||
std::vector<Part> _parts;
|
||||
bool _needBubble = false;
|
||||
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -21,6 +21,12 @@ struct HistoryMessageVia;
|
|||
struct HistoryMessageReply;
|
||||
struct HistoryMessageForwarded;
|
||||
|
||||
namespace Data {
|
||||
enum class CallFinishReason : char;
|
||||
struct Invoice;
|
||||
struct Call;
|
||||
} // namespace Data
|
||||
|
||||
namespace Media {
|
||||
namespace Clip {
|
||||
class Playback;
|
||||
|
@ -35,12 +41,6 @@ TextWithEntities WithCaptionSelectedText(
|
|||
const QString &attachType,
|
||||
const Text &caption,
|
||||
TextSelection selection);
|
||||
QString WithCaptionNotificationText(
|
||||
const QString &attachType,
|
||||
const Text &caption);
|
||||
QString WithCaptionDialogsText(
|
||||
const QString &attachType,
|
||||
const Text &caption);
|
||||
|
||||
class HistoryFileMedia : public HistoryMedia {
|
||||
public:
|
||||
|
@ -56,7 +56,7 @@ public:
|
|||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) 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 {
|
||||
return true;
|
||||
|
@ -128,22 +128,22 @@ protected:
|
|||
class HistoryPhoto : public HistoryFileMedia {
|
||||
public:
|
||||
HistoryPhoto(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<Element*> parent,
|
||||
not_null<PhotoData*> photo,
|
||||
const QString &caption);
|
||||
HistoryPhoto(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<Element*> parent,
|
||||
not_null<PeerData*> chat,
|
||||
not_null<PhotoData*> photo,
|
||||
int width);
|
||||
HistoryPhoto(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<Element*> parent,
|
||||
not_null<PeerData*> chat,
|
||||
const MTPDphoto &photo,
|
||||
int width);
|
||||
HistoryPhoto(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<HistoryItem*> realParent,
|
||||
not_null<Element*> parent,
|
||||
not_null<Element*> realParent,
|
||||
const HistoryPhoto &other);
|
||||
|
||||
void init();
|
||||
|
@ -151,8 +151,8 @@ public:
|
|||
return MediaTypePhoto;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
not_null<Element*> newParent,
|
||||
not_null<Element*> realParent) const override {
|
||||
return std::make_unique<HistoryPhoto>(newParent, realParent, *this);
|
||||
}
|
||||
|
||||
|
@ -171,12 +171,8 @@ public:
|
|||
return !_caption.isEmpty();
|
||||
}
|
||||
|
||||
QString notificationText() const override;
|
||||
QString inDialogsText() const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
||||
|
||||
PhotoData *getPhoto() const override {
|
||||
return _data;
|
||||
}
|
||||
|
@ -199,12 +195,6 @@ public:
|
|||
QPoint point,
|
||||
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 {
|
||||
return !_data->thumb->isNull();
|
||||
}
|
||||
|
@ -220,9 +210,6 @@ public:
|
|||
bool skipBubbleTail() const override {
|
||||
return isBubbleBottom() && _caption.isEmpty();
|
||||
}
|
||||
bool canEditCaption() const override {
|
||||
return true;
|
||||
}
|
||||
bool isReadyForOpen() const override {
|
||||
return _data->loaded();
|
||||
}
|
||||
|
@ -254,20 +241,20 @@ private:
|
|||
class HistoryVideo : public HistoryFileMedia {
|
||||
public:
|
||||
HistoryVideo(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<Element*> parent,
|
||||
not_null<DocumentData*> document,
|
||||
const QString &caption);
|
||||
HistoryVideo(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<HistoryItem*> realParent,
|
||||
not_null<Element*> parent,
|
||||
not_null<Element*> realParent,
|
||||
const HistoryVideo &other);
|
||||
|
||||
HistoryMediaType type() const override {
|
||||
return MediaTypeVideo;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
not_null<Element*> newParent,
|
||||
not_null<Element*> realParent) const override {
|
||||
return std::make_unique<HistoryVideo>(newParent, realParent, *this);
|
||||
}
|
||||
|
||||
|
@ -286,12 +273,8 @@ public:
|
|||
return !_caption.isEmpty();
|
||||
}
|
||||
|
||||
QString notificationText() const override;
|
||||
QString inDialogsText() const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
||||
|
||||
DocumentData *getDocument() const override {
|
||||
return _data;
|
||||
}
|
||||
|
@ -318,12 +301,6 @@ public:
|
|||
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 {
|
||||
return !_data->thumb->isNull();
|
||||
}
|
||||
|
@ -339,9 +316,6 @@ public:
|
|||
bool skipBubbleTail() const override {
|
||||
return isBubbleBottom() && _caption.isEmpty();
|
||||
}
|
||||
bool canEditCaption() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
float64 dataProgress() const override;
|
||||
|
@ -366,15 +340,14 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class HistoryDocument : public HistoryFileMedia, public RuntimeComposer {
|
||||
class HistoryDocument
|
||||
: public HistoryFileMedia
|
||||
, public RuntimeComposer<HistoryDocument> {
|
||||
public:
|
||||
HistoryDocument(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<Element*> parent,
|
||||
not_null<DocumentData*> document,
|
||||
const QString &caption);
|
||||
HistoryDocument(
|
||||
not_null<HistoryItem*> parent,
|
||||
const HistoryDocument &other);
|
||||
|
||||
HistoryMediaType type() const override {
|
||||
return _data->isVoiceMessage()
|
||||
|
@ -383,13 +356,6 @@ public:
|
|||
? MediaTypeMusicFile
|
||||
: 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;
|
||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||
|
@ -401,12 +367,8 @@ public:
|
|||
uint16 fullSelectionLength() const override;
|
||||
bool hasTextForCopy() const override;
|
||||
|
||||
QString notificationText() const override;
|
||||
QString inDialogsText() const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
||||
|
||||
bool uploading() const override {
|
||||
return _data->uploading();
|
||||
}
|
||||
|
@ -417,12 +379,6 @@ public:
|
|||
|
||||
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 {
|
||||
return !_data->thumb->isNull();
|
||||
}
|
||||
|
@ -439,15 +395,12 @@ public:
|
|||
bool hideForwardedFrom() const override {
|
||||
return _data->isSong();
|
||||
}
|
||||
bool canEditCaption() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void step_voiceProgress(float64 ms, bool timer);
|
||||
|
||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||
|
||||
void refreshParentId(not_null<HistoryItem*> realParent) override;
|
||||
void refreshParentId(not_null<Element*> realParent) override;
|
||||
|
||||
protected:
|
||||
float64 dataProgress() const override;
|
||||
|
@ -476,21 +429,13 @@ private:
|
|||
class HistoryGif : public HistoryFileMedia {
|
||||
public:
|
||||
HistoryGif(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<Element*> parent,
|
||||
not_null<DocumentData*> document,
|
||||
const QString &caption);
|
||||
HistoryGif(not_null<HistoryItem*> parent, const HistoryGif &other);
|
||||
|
||||
HistoryMediaType type() const override {
|
||||
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;
|
||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||
|
@ -507,12 +452,8 @@ public:
|
|||
return !_caption.isEmpty();
|
||||
}
|
||||
|
||||
QString notificationText() const override;
|
||||
QString inDialogsText() const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
||||
|
||||
bool uploading() const override {
|
||||
return _data->uploading();
|
||||
}
|
||||
|
@ -528,12 +469,6 @@ public:
|
|||
void stopInline() 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 {
|
||||
return !_data->thumb->isNull();
|
||||
}
|
||||
|
@ -551,9 +486,6 @@ public:
|
|||
bool skipBubbleTail() const override {
|
||||
return isBubbleBottom() && _caption.isEmpty();
|
||||
}
|
||||
bool canEditCaption() const override {
|
||||
return !_data->isVideoMessage();
|
||||
}
|
||||
bool isReadyForOpen() const override {
|
||||
return _data->loaded();
|
||||
}
|
||||
|
@ -600,19 +532,12 @@ private:
|
|||
class HistorySticker : public HistoryMedia {
|
||||
public:
|
||||
HistorySticker(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<Element*> parent,
|
||||
not_null<DocumentData*> document);
|
||||
|
||||
HistoryMediaType type() const override {
|
||||
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;
|
||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||
|
@ -627,19 +552,12 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
QString notificationText() const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
DocumentData *getDocument() const override {
|
||||
return _data;
|
||||
}
|
||||
|
||||
void attachToParent() override;
|
||||
void detachFromParent() override;
|
||||
|
||||
void updateSentMedia(const MTPMessageMedia &media) override;
|
||||
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
|
||||
|
||||
bool hasReplyPreview() const override {
|
||||
return !_data->thumb->isNull();
|
||||
}
|
||||
|
@ -674,8 +592,8 @@ private:
|
|||
class HistoryContact : public HistoryMedia {
|
||||
public:
|
||||
HistoryContact(
|
||||
not_null<HistoryItem*> parent,
|
||||
int32 userId,
|
||||
not_null<Element*> parent,
|
||||
UserId userId,
|
||||
const QString &first,
|
||||
const QString &last,
|
||||
const QString &phone);
|
||||
|
@ -683,18 +601,6 @@ public:
|
|||
HistoryMediaType type() const override {
|
||||
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;
|
||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||
|
@ -706,14 +612,11 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
QString notificationText() const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
void attachToParent() override;
|
||||
void detachFromParent() override;
|
||||
|
||||
void updateSentMedia(const MTPMessageMedia &media) override;
|
||||
|
||||
bool needsBubble() const override {
|
||||
return true;
|
||||
}
|
||||
|
@ -736,7 +639,7 @@ public:
|
|||
private:
|
||||
QSize countOptimalSize() override;
|
||||
|
||||
int32 _userId = 0;
|
||||
UserId _userId = 0;
|
||||
UserData *_contact = nullptr;
|
||||
|
||||
int _phonew = 0;
|
||||
|
@ -753,17 +656,12 @@ private:
|
|||
class HistoryCall : public HistoryMedia {
|
||||
public:
|
||||
HistoryCall(
|
||||
not_null<HistoryItem*> parent,
|
||||
const MTPDmessageActionPhoneCall &call);
|
||||
not_null<Element*> parent,
|
||||
not_null<Data::Call*> call);
|
||||
|
||||
HistoryMediaType type() const override {
|
||||
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;
|
||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||
|
@ -775,7 +673,6 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
QString notificationText() const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
bool needsBubble() const override {
|
||||
|
@ -785,22 +682,14 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
enum class FinishReason {
|
||||
Missed,
|
||||
Busy,
|
||||
Disconnected,
|
||||
Hangup,
|
||||
};
|
||||
FinishReason reason() const {
|
||||
return _reason;
|
||||
}
|
||||
Data::CallFinishReason reason() const;
|
||||
|
||||
private:
|
||||
using FinishReason = Data::CallFinishReason;
|
||||
|
||||
QSize countOptimalSize() override;
|
||||
|
||||
static FinishReason GetReason(const MTPDmessageActionPhoneCall &call);
|
||||
|
||||
FinishReason _reason = FinishReason::Missed;
|
||||
FinishReason _reason;
|
||||
int _duration = 0;
|
||||
|
||||
QString _text;
|
||||
|
@ -813,24 +702,14 @@ private:
|
|||
class HistoryWebPage : public HistoryMedia {
|
||||
public:
|
||||
HistoryWebPage(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<Element*> parent,
|
||||
not_null<WebPageData*> data);
|
||||
HistoryWebPage(
|
||||
not_null<HistoryItem*> parent,
|
||||
const HistoryWebPage &other);
|
||||
|
||||
HistoryMediaType type() const override {
|
||||
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<HistoryItem*> realParent) override;
|
||||
void refreshParentId(not_null<Element*> realParent) override;
|
||||
|
||||
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||
|
@ -930,21 +809,16 @@ private:
|
|||
|
||||
class HistoryGame : public HistoryMedia {
|
||||
public:
|
||||
HistoryGame(not_null<HistoryItem*> parent, not_null<GameData*> data);
|
||||
HistoryGame(not_null<HistoryItem*> parent, const HistoryGame &other);
|
||||
HistoryGame(
|
||||
not_null<Element*> parent,
|
||||
not_null<GameData*> data,
|
||||
const TextWithEntities &consumed);
|
||||
|
||||
HistoryMediaType type() const override {
|
||||
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<HistoryItem*> realParent) override;
|
||||
void refreshParentId(not_null<Element*> realParent) override;
|
||||
|
||||
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||
|
@ -961,7 +835,6 @@ public:
|
|||
bool hasTextForCopy() const override {
|
||||
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 {
|
||||
return _attach && _attach->toggleSelectionByHandlerClick(p);
|
||||
|
@ -970,7 +843,6 @@ public:
|
|||
return _attach && _attach->dragItemByHandler(p);
|
||||
}
|
||||
|
||||
QString notificationText() const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||
|
@ -1004,9 +876,6 @@ public:
|
|||
return _data;
|
||||
}
|
||||
|
||||
void updateSentMedia(const MTPMessageMedia &media) override;
|
||||
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
|
||||
|
||||
bool needsBubble() const override {
|
||||
return true;
|
||||
}
|
||||
|
@ -1045,24 +914,14 @@ private:
|
|||
class HistoryInvoice : public HistoryMedia {
|
||||
public:
|
||||
HistoryInvoice(
|
||||
not_null<HistoryItem*> parent,
|
||||
const MTPDmessageMediaInvoice &data);
|
||||
HistoryInvoice(
|
||||
not_null<HistoryItem*> parent,
|
||||
const HistoryInvoice &other);
|
||||
not_null<Element*> parent,
|
||||
not_null<Data::Invoice*> invoice);
|
||||
|
||||
HistoryMediaType type() const override {
|
||||
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<HistoryItem*> realParent) override;
|
||||
void refreshParentId(not_null<Element*> realParent) override;
|
||||
|
||||
MsgId getReceiptMsgId() const {
|
||||
return _receiptMsgId;
|
||||
|
@ -1092,7 +951,6 @@ public:
|
|||
return _attach && _attach->dragItemByHandler(p);
|
||||
}
|
||||
|
||||
QString notificationText() const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||
|
@ -1123,7 +981,7 @@ private:
|
|||
QSize countOptimalSize() override;
|
||||
QSize countCurrentSize(int newWidth) override;
|
||||
|
||||
void fillFromData(const MTPDmessageMediaInvoice &data);
|
||||
void fillFromData(not_null<Data::Invoice*> invoice);
|
||||
|
||||
TextSelection toDescriptionSelection(TextSelection selection) const;
|
||||
TextSelection fromDescriptionSelection(TextSelection selection) const;
|
||||
|
@ -1148,24 +1006,14 @@ struct LocationData;
|
|||
class HistoryLocation : public HistoryMedia {
|
||||
public:
|
||||
HistoryLocation(
|
||||
not_null<HistoryItem*> parent,
|
||||
const LocationCoords &coords,
|
||||
not_null<Element*> parent,
|
||||
not_null<LocationData*> location,
|
||||
const QString &title = QString(),
|
||||
const QString &description = QString());
|
||||
HistoryLocation(
|
||||
not_null<HistoryItem*> parent,
|
||||
const HistoryLocation &other);
|
||||
|
||||
HistoryMediaType type() const override {
|
||||
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;
|
||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||
|
@ -1187,8 +1035,6 @@ public:
|
|||
return p == _link;
|
||||
}
|
||||
|
||||
QString notificationText() const override;
|
||||
QString inDialogsText() const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
bool needsBubble() const override;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -22,6 +22,7 @@ QString GetErrorTextForForward(
|
|||
not_null<PeerData*> peer,
|
||||
const HistoryItemsList &items);
|
||||
void FastShareMessage(not_null<HistoryItem*> item);
|
||||
QString FormatViewsCount(int views);
|
||||
|
||||
class HistoryMessage
|
||||
: public HistoryItem
|
||||
|
@ -142,37 +143,22 @@ public:
|
|||
markup);
|
||||
}
|
||||
|
||||
void initTime();
|
||||
void initMedia(const MTPMessageMedia *media);
|
||||
void initMediaFromDocument(DocumentData *doc, const QString &caption);
|
||||
|
||||
int32 plainMaxWidth() const;
|
||||
|
||||
bool drawBubble() const;
|
||||
bool hasBubble() const override {
|
||||
return drawBubble();
|
||||
}
|
||||
bool hasFastReply() const;
|
||||
bool displayFastReply() const;
|
||||
bool displayForwardedFrom() const;
|
||||
bool allowsForward() const override;
|
||||
bool allowsEdit(const QDateTime &now) const override;
|
||||
bool uploading() const;
|
||||
bool displayRightAction() const override;
|
||||
|
||||
void applyGroupAdminChanges(
|
||||
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 setRealId(MsgId newId) override;
|
||||
ClickHandlerPtr rightActionLink() const 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;
|
||||
|
||||
void applyEdition(const MTPDmessage &message) override;
|
||||
|
@ -186,20 +172,10 @@ public:
|
|||
void eraseFromUnreadMentions() override;
|
||||
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
||||
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
void setText(const TextWithEntities &textWithEntities) override;
|
||||
TextWithEntities originalText() 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;
|
||||
not_null<PeerData*> displayFrom() const;
|
||||
bool updateDependencyItem() override;
|
||||
|
@ -220,9 +196,6 @@ public:
|
|||
|
||||
~HistoryMessage();
|
||||
|
||||
protected:
|
||||
void refreshEditedBadge() override;
|
||||
|
||||
private:
|
||||
HistoryMessage(
|
||||
not_null<History*> history,
|
||||
|
@ -295,16 +268,9 @@ private:
|
|||
void replaceBuyWithReceiptInMarkup();
|
||||
|
||||
void applyEditionToEmpty();
|
||||
QDateTime displayedEditDate(bool hasViaBotOrInlineMarkup) const;
|
||||
const HistoryMessageEdited *displayedEditBadge() const;
|
||||
HistoryMessageEdited *displayedEditBadge();
|
||||
|
||||
void setMedia(const MTPMessageMedia *media);
|
||||
void setReplyMarkup(const MTPReplyMarkup *markup);
|
||||
|
||||
bool displayFastShare() const;
|
||||
bool displayGoToOriginal() const;
|
||||
|
||||
struct CreateConfig;
|
||||
void createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, UserId viaBotId, const QString &postAuthor, const MTPReplyMarkup &markup);
|
||||
void createComponents(const CreateConfig &config);
|
||||
|
@ -315,8 +281,6 @@ private:
|
|||
QString _timeText;
|
||||
int _timeWidth = 0;
|
||||
|
||||
mutable ClickHandlerPtr _rightActionLink;
|
||||
mutable ClickHandlerPtr _fastReplyLink;
|
||||
mutable int32 _fromNameVersion = 0;
|
||||
|
||||
friend class HistoryView::Element;
|
||||
|
|
|
@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_service_message.h"
|
||||
#include "data/data_feed.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
|
@ -204,7 +205,10 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
|||
case mtpc_messageActionChatEditPhoto: {
|
||||
auto &photo = action.c_messageActionChatEditPhoto().vphoto;
|
||||
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;
|
||||
|
||||
|
@ -268,42 +272,12 @@ HistoryService::PreparedText HistoryService::preparePinnedText() {
|
|||
auto result = PreparedText {};
|
||||
auto pinned = Get<HistoryServicePinned>();
|
||||
if (pinned && pinned->msg) {
|
||||
auto mediaText = ([pinned]() -> QString {
|
||||
auto media = pinned->msg->getMedia();
|
||||
switch (media ? media->type() : MediaTypeCount) {
|
||||
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;
|
||||
const auto mediaText = [&] {
|
||||
if (const auto media = pinned->msg->media()) {
|
||||
return media->pinnedTextSubstring();
|
||||
}
|
||||
return QString();
|
||||
})();
|
||||
|
||||
}();
|
||||
result.links.push_back(fromLink());
|
||||
result.links.push_back(pinned->lnk);
|
||||
if (mediaText.isEmpty()) {
|
||||
|
@ -344,8 +318,8 @@ HistoryService::PreparedText HistoryService::prepareGameScoreText() {
|
|||
|
||||
auto computeGameTitle = [gamescore, &result]() -> QString {
|
||||
if (gamescore && gamescore->msg) {
|
||||
if (const auto media = gamescore->msg->getMedia()) {
|
||||
if (media->type() == MediaTypeGame) {
|
||||
if (const auto media = gamescore->msg->media()) {
|
||||
if (const auto game = media->game()) {
|
||||
const auto row = 0;
|
||||
const auto column = 0;
|
||||
result.links.push_back(
|
||||
|
@ -353,7 +327,7 @@ HistoryService::PreparedText HistoryService::prepareGameScoreText() {
|
|||
row,
|
||||
column,
|
||||
gamescore->msg->fullId()));
|
||||
auto titleText = static_cast<HistoryGame*>(media)->game()->title;
|
||||
auto titleText = game->title;
|
||||
return textcmdLink(result.links.size(), titleText);
|
||||
}
|
||||
}
|
||||
|
@ -404,11 +378,11 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() {
|
|||
auto result = PreparedText {};
|
||||
auto payment = Get<HistoryServicePayment>();
|
||||
|
||||
auto invoiceTitle = ([payment]() -> QString {
|
||||
auto invoiceTitle = [&] {
|
||||
if (payment && payment->msg) {
|
||||
if (auto media = payment->msg->getMedia()) {
|
||||
if (media->type() == MediaTypeInvoice) {
|
||||
return static_cast<HistoryInvoice*>(media)->getTitle();
|
||||
if (const auto media = payment->msg->media()) {
|
||||
if (const auto invoice = media->invoice()) {
|
||||
return invoice->title;
|
||||
}
|
||||
}
|
||||
return lang(lng_deleted_message);
|
||||
|
@ -416,7 +390,7 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() {
|
|||
return lang(lng_contacts_loading);
|
||||
}
|
||||
return QString();
|
||||
})();
|
||||
}();
|
||||
|
||||
if (invoiceTitle.isEmpty()) {
|
||||
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);
|
||||
}
|
||||
|
||||
HistoryService::HistoryService(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) {
|
||||
HistoryService::HistoryService(
|
||||
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);
|
||||
}
|
||||
|
||||
HistoryService::HistoryService(not_null<History*> history, MsgId msgId, QDateTime date, const PreparedText &message, MTPDmessage::Flags flags, int32 from, PhotoData *photo) :
|
||||
HistoryItem(history, msgId, flags, date, from) {
|
||||
HistoryService::HistoryService(
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
TextWithEntities HistoryService::selectedText(TextSelection selection) const {
|
||||
return _text.originalTextWithEntities((selection == FullSelection) ? AllTextSelection : selection);
|
||||
}
|
||||
|
||||
QString HistoryService::inDialogsText(DrawInDialog way) const {
|
||||
return textcmdLink(1, TextUtilities::Clean(notificationText()));
|
||||
}
|
||||
|
@ -599,16 +586,14 @@ void HistoryService::applyEdition(const MTPDmessageService &message) {
|
|||
void HistoryService::removeMedia() {
|
||||
if (!_media) return;
|
||||
|
||||
bool mediaWasDisplayed = _media->isDisplayed();
|
||||
_media.reset();
|
||||
if (mediaWasDisplayed) {
|
||||
_textWidth = -1;
|
||||
_textHeight = 0;
|
||||
}
|
||||
_textWidth = -1;
|
||||
_textHeight = 0;
|
||||
Auth().data().requestItemViewResize(this);
|
||||
}
|
||||
|
||||
Storage::SharedMediaTypesMask HistoryService::sharedMediaTypes() const {
|
||||
if (auto media = getMedia()) {
|
||||
if (auto media = this->media()) {
|
||||
return media->sharedMediaTypes();
|
||||
}
|
||||
return {};
|
||||
|
|
|
@ -20,24 +20,24 @@ struct HistoryServiceDependentData {
|
|||
};
|
||||
|
||||
struct HistoryServicePinned
|
||||
: public RuntimeComponent<HistoryServicePinned>
|
||||
: public RuntimeComponent<HistoryServicePinned, HistoryItem>
|
||||
, public HistoryServiceDependentData {
|
||||
};
|
||||
|
||||
struct HistoryServiceGameScore
|
||||
: public RuntimeComponent<HistoryServiceGameScore>
|
||||
: public RuntimeComponent<HistoryServiceGameScore, HistoryItem>
|
||||
, public HistoryServiceDependentData {
|
||||
int score = 0;
|
||||
};
|
||||
|
||||
struct HistoryServicePayment
|
||||
: public RuntimeComponent<HistoryServicePayment>
|
||||
: public RuntimeComponent<HistoryServicePayment, HistoryItem>
|
||||
, public HistoryServiceDependentData {
|
||||
QString amount;
|
||||
};
|
||||
|
||||
struct HistoryServiceSelfDestruct
|
||||
: public RuntimeComponent<HistoryServiceSelfDestruct> {
|
||||
: public RuntimeComponent<HistoryServiceSelfDestruct, HistoryItem> {
|
||||
enum class Type {
|
||||
Photo,
|
||||
Video,
|
||||
|
@ -82,12 +82,6 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] TextSelection adjustSelection(
|
||||
TextSelection selection,
|
||||
TextSelectType type) const override {
|
||||
return _text.adjustSelection(selection, type);
|
||||
}
|
||||
|
||||
void applyEdition(const MTPDmessageService &message) override;
|
||||
TimeMs getSelfDestructIn(TimeMs now) override;
|
||||
|
||||
|
@ -99,7 +93,6 @@ public:
|
|||
bool serviceMsg() const override {
|
||||
return true;
|
||||
}
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
QString inDialogsText(DrawInDialog way) const override;
|
||||
QString inReplyText() const override;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "inline_bots/inline_bot_result.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_message.h"
|
||||
|
@ -648,8 +649,10 @@ HistoryWidget::HistoryWidget(QWidget *parent, not_null<Window::Controller*> cont
|
|||
Auth().data().itemLayoutChanged(
|
||||
) | rpl::start_with_next([this](auto item) {
|
||||
if (_peer && _list) {
|
||||
if (item->isUnderCursor()) {
|
||||
_list->onUpdateSelected();
|
||||
if (const auto view = item->mainView()) {
|
||||
if (view->isUnderCursor()) {
|
||||
_list->onUpdateSelected();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, lifetime());
|
||||
|
@ -714,7 +717,10 @@ void HistoryWidget::animatedScrollToItem(MsgId msgId) {
|
|||
return;
|
||||
}
|
||||
|
||||
auto scrollTo = snap(itemTopForHighlight(to), 0, _scroll->scrollTopMax());
|
||||
auto scrollTo = snap(
|
||||
itemTopForHighlight(to->mainView()),
|
||||
0,
|
||||
_scroll->scrollTopMax());
|
||||
animatedScrollToY(scrollTo, to);
|
||||
}
|
||||
|
||||
|
@ -769,9 +775,10 @@ void HistoryWidget::scrollToAnimationCallback(FullMsgId attachToId) {
|
|||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::enqueueMessageHighlight(not_null<HistoryItem*> item) {
|
||||
if (const auto group = item->getFullGroup()) {
|
||||
item = group->leader;
|
||||
void HistoryWidget::enqueueMessageHighlight(
|
||||
not_null<HistoryView::Element*> view) {
|
||||
if (const auto group = view->getFullGroup()) {
|
||||
view = group->leader;
|
||||
}
|
||||
auto enqueueMessageId = [this](MsgId universalId) {
|
||||
if (_highlightQueue.empty() && !_highlightTimer.isActive()) {
|
||||
|
@ -782,6 +789,7 @@ void HistoryWidget::enqueueMessageHighlight(not_null<HistoryItem*> item) {
|
|||
checkNextHighlight();
|
||||
}
|
||||
};
|
||||
const auto item = view->data();
|
||||
if (item->history() == _history) {
|
||||
enqueueMessageId(item->id);
|
||||
} else if (item->history() == _migrated) {
|
||||
|
@ -874,12 +882,10 @@ void HistoryWidget::clearHighlightMessages() {
|
|||
stopMessageHighlight();
|
||||
}
|
||||
|
||||
int HistoryWidget::itemTopForHighlight(not_null<HistoryItem*> item) const {
|
||||
const auto view = item->mainView();
|
||||
Assert(view != nullptr);
|
||||
|
||||
if (const auto group = item->getFullGroup()) {
|
||||
item = group->leader;
|
||||
int HistoryWidget::itemTopForHighlight(
|
||||
not_null<HistoryView::Element*> view) const {
|
||||
if (const auto group = view->getFullGroup()) {
|
||||
view = group->leader;
|
||||
}
|
||||
auto itemTop = _list->itemTop(view);
|
||||
Assert(itemTop >= 0);
|
||||
|
@ -4354,8 +4360,8 @@ void HistoryWidget::onThumbDocumentUploaded(
|
|||
|
||||
void HistoryWidget::onPhotoProgress(const FullMsgId &newId) {
|
||||
if (const auto item = App::histItemById(newId)) {
|
||||
const auto photo = item->getMedia()
|
||||
? item->getMedia()->getPhoto()
|
||||
const auto photo = item->media()
|
||||
? item->media()->photo()
|
||||
: nullptr;
|
||||
updateSendAction(item->history(), SendAction::Type::UploadPhoto, 0);
|
||||
Auth().data().requestItemRepaint(item);
|
||||
|
@ -4364,8 +4370,8 @@ void HistoryWidget::onPhotoProgress(const FullMsgId &newId) {
|
|||
|
||||
void HistoryWidget::onDocumentProgress(const FullMsgId &newId) {
|
||||
if (const auto item = App::histItemById(newId)) {
|
||||
const auto media = item->getMedia();
|
||||
const auto document = media ? media->getDocument() : nullptr;
|
||||
const auto media = item->media();
|
||||
const auto document = media ? media->document() : nullptr;
|
||||
const auto sendAction = (document && document->isVoiceMessage())
|
||||
? SendAction::Type::UploadVoice
|
||||
: SendAction::Type::UploadFile;
|
||||
|
@ -4389,8 +4395,8 @@ void HistoryWidget::onPhotoFailed(const FullMsgId &newId) {
|
|||
|
||||
void HistoryWidget::onDocumentFailed(const FullMsgId &newId) {
|
||||
if (const auto item = App::histItemById(newId)) {
|
||||
const auto media = item->getMedia();
|
||||
const auto document = media ? media->getDocument() : nullptr;
|
||||
const auto media = item->media();
|
||||
const auto document = media ? media->document() : nullptr;
|
||||
const auto sendAction = (document && document->isVoiceMessage())
|
||||
? SendAction::Type::UploadVoice
|
||||
: SendAction::Type::UploadFile;
|
||||
|
@ -4666,8 +4672,11 @@ int HistoryWidget::countInitialScrollTop() {
|
|||
setMsgId(0);
|
||||
return countInitialScrollTop();
|
||||
} else {
|
||||
result = itemTopForHighlight(item);
|
||||
enqueueMessageHighlight(item);
|
||||
const auto view = item->mainView();
|
||||
Assert(view != nullptr);
|
||||
|
||||
result = itemTopForHighlight(view);
|
||||
enqueueMessageHighlight(view);
|
||||
}
|
||||
} else if (_history->unreadBar || (_migrated && _migrated->unreadBar)) {
|
||||
result = unreadBarTop();
|
||||
|
@ -5076,7 +5085,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
|
|||
}
|
||||
} else if (e->key() == Qt::Key_Up) {
|
||||
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) {
|
||||
editMessage(_history->lastSentMsg);
|
||||
return;
|
||||
|
@ -5598,8 +5607,8 @@ void HistoryWidget::editMessage(FullMsgId itemId) {
|
|||
}
|
||||
|
||||
void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
|
||||
if (const auto media = item->getMedia()) {
|
||||
if (media->canEditCaption()) {
|
||||
if (const auto media = item->media()) {
|
||||
if (media->allowsEditCaption()) {
|
||||
Ui::show(Box<EditCaptionBox>(media, item->fullId()));
|
||||
return;
|
||||
}
|
||||
|
@ -5635,9 +5644,9 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
|
|||
applyDraft(false);
|
||||
|
||||
_previewData = nullptr;
|
||||
if (const auto media = item->getMedia()) {
|
||||
if (media->type() == MediaTypeWebPage) {
|
||||
_previewData = static_cast<HistoryWebPage*>(media)->webpage();
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto page = media->webpage()) {
|
||||
_previewData = page;
|
||||
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());
|
||||
if (!drawWebPagePreview) {
|
||||
if (drawMsgText) {
|
||||
if (drawMsgText->getMedia() && drawMsgText->getMedia()->hasReplyPreview()) {
|
||||
auto replyPreview = drawMsgText->getMedia()->replyPreview();
|
||||
if (drawMsgText->media() && drawMsgText->media()->hasReplyPreview()) {
|
||||
auto replyPreview = drawMsgText->media()->replyPreview();
|
||||
if (!replyPreview->isNull()) {
|
||||
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));
|
||||
|
@ -6362,7 +6371,7 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
|
|||
st::historyForwardIcon.paint(p, st::historyReplyIconPosition + QPoint(0, backy), width());
|
||||
if (!drawWebPagePreview) {
|
||||
const auto firstItem = _toForward.front();
|
||||
const auto firstMedia = firstItem->getMedia();
|
||||
const auto firstMedia = firstItem->media();
|
||||
const auto serviceColor = (_toForward.size() > 1)
|
||||
|| (firstMedia != nullptr)
|
||||
|| firstItem->serviceMsg();
|
||||
|
@ -6498,8 +6507,8 @@ void HistoryWidget::drawPinnedBar(Painter &p) {
|
|||
|
||||
int32 left = st::msgReplyBarSkip + st::msgReplyBarSkip;
|
||||
if (_pinnedBar->msg) {
|
||||
if (_pinnedBar->msg->getMedia() && _pinnedBar->msg->getMedia()->hasReplyPreview()) {
|
||||
ImagePtr replyPreview = _pinnedBar->msg->getMedia()->replyPreview();
|
||||
if (_pinnedBar->msg->media() && _pinnedBar->msg->media()->hasReplyPreview()) {
|
||||
ImagePtr replyPreview = _pinnedBar->msg->media()->replyPreview();
|
||||
if (!replyPreview->isNull()) {
|
||||
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));
|
||||
|
|
|
@ -246,7 +246,7 @@ public:
|
|||
|
||||
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;
|
||||
|
||||
MessageIdsList getSelectedItems() const;
|
||||
|
@ -673,7 +673,7 @@ private:
|
|||
// Counts scrollTop for placing the scroll right at the unread
|
||||
// messages bar, choosing from _history and _migrated unreadBar.
|
||||
int unreadBarTop() const;
|
||||
int itemTopForHighlight(not_null<HistoryItem*> item) const;
|
||||
int itemTopForHighlight(not_null<HistoryView::Element*> view) const;
|
||||
void scrollToCurrentVoiceMessage(FullMsgId fromId, FullMsgId toId);
|
||||
|
||||
// Scroll to current y without updating the _lastUserScrolled time.
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_cursor_state.h"
|
||||
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
|
||||
HistoryTextState::HistoryTextState(not_null<const HistoryItem*> item)
|
||||
: itemId(item->fullId()) {
|
||||
|
@ -31,3 +32,21 @@ HistoryTextState::HistoryTextState(
|
|||
: itemId(item->fullId())
|
||||
, 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) {
|
||||
}
|
||||
|
|
|
@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
namespace HistoryView {
|
||||
class Element;
|
||||
} // namespace HistoryView
|
||||
|
||||
class HistoryItem;
|
||||
|
||||
enum HistoryCursorState {
|
||||
|
@ -25,6 +29,13 @@ struct HistoryTextState {
|
|||
HistoryTextState(
|
||||
not_null<const HistoryItem*> item,
|
||||
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(
|
||||
std::nullptr_t,
|
||||
const Text::StateResult &state)
|
||||
|
@ -56,7 +67,7 @@ struct HistoryStateRequest {
|
|||
}
|
||||
};
|
||||
|
||||
enum InfoDisplayType {
|
||||
enum InfoDisplayType : char {
|
||||
InfoDisplayDefault,
|
||||
InfoDisplayOverImage,
|
||||
InfoDisplayOverBackground,
|
||||
|
|
|
@ -8,10 +8,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_element.h"
|
||||
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_media.h"
|
||||
#include "history/history_media_grouped.h"
|
||||
#include "history/history.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "auth_session.h"
|
||||
#include "layout.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
@ -20,15 +24,51 @@ namespace {
|
|||
constexpr int kAttachMessageToPreviousSecondsDelta = 900;
|
||||
|
||||
} // 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)
|
||||
: _data(data)
|
||||
, _media(_data->media() ? _data->media()->createView(this) : nullptr)
|
||||
, _context(context) {
|
||||
Auth().data().registerItemView(this);
|
||||
initGroup();
|
||||
}
|
||||
|
||||
not_null<HistoryItem*> Element::data() const {
|
||||
return _data;
|
||||
}
|
||||
|
||||
HistoryMedia *Element::media() const {
|
||||
return _media.get();
|
||||
}
|
||||
|
||||
Context Element::context() const {
|
||||
return _context;
|
||||
}
|
||||
|
@ -44,7 +84,7 @@ void Element::setY(int y) {
|
|||
int Element::marginTop() const {
|
||||
const auto item = data();
|
||||
auto result = 0;
|
||||
if (!item->isHiddenByGroup()) {
|
||||
if (!isHiddenByGroup()) {
|
||||
if (isAttachedToPrevious()) {
|
||||
result += st::msgMarginTopAttached;
|
||||
} else {
|
||||
|
@ -60,7 +100,11 @@ int Element::marginTop() const {
|
|||
|
||||
int Element::marginBottom() const {
|
||||
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() {
|
||||
|
@ -82,6 +126,113 @@ bool Element::isAttachedToNext() const {
|
|||
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() {
|
||||
recountDisplayDateInBlocks();
|
||||
recountAttachToPreviousInBlocks();
|
||||
|
@ -92,6 +243,17 @@ void Element::nextInBlocksChanged() {
|
|||
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) {
|
||||
const auto item = data();
|
||||
if (!item->Has<HistoryMessageDate>() && !item->Has<HistoryMessageUnreadBar>()) {
|
||||
|
@ -196,6 +358,53 @@ bool Element::displayFromName() const {
|
|||
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() {
|
||||
return _block;
|
||||
}
|
||||
|
@ -261,6 +470,29 @@ Element *Element::nextInBlocks() const {
|
|||
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(
|
||||
const ClickHandlerPtr &handler,
|
||||
bool active) {
|
||||
|
@ -271,7 +503,7 @@ void Element::clickHandlerActiveChanged(
|
|||
}
|
||||
App::hoveredLinkItem(active ? this : nullptr);
|
||||
Auth().data().requestItemRepaint(_data);
|
||||
if (const auto media = _data->getMedia()) {
|
||||
if (const auto media = this->media()) {
|
||||
media->clickHandlerActiveChanged(handler, active);
|
||||
}
|
||||
}
|
||||
|
@ -286,13 +518,13 @@ void Element::clickHandlerPressedChanged(
|
|||
}
|
||||
App::pressedLinkItem(pressed ? this : nullptr);
|
||||
Auth().data().requestItemRepaint(_data);
|
||||
if (const auto media = _data->getMedia()) {
|
||||
if (const auto media = this->media()) {
|
||||
media->clickHandlerPressedChanged(handler, pressed);
|
||||
}
|
||||
}
|
||||
|
||||
Element::~Element() {
|
||||
App::messageViewDestroyed(this);
|
||||
Auth().data().unregisterItemView(this);
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -13,11 +13,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
class HistoryBlock;
|
||||
class HistoryItem;
|
||||
class HistoryMedia;
|
||||
class HistoryWebPage;
|
||||
struct HistoryTextState;
|
||||
struct HistoryStateRequest;
|
||||
enum InfoDisplayType : char;
|
||||
|
||||
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 {
|
||||
History,
|
||||
Feed,
|
||||
|
@ -26,7 +49,7 @@ enum class Context : char {
|
|||
|
||||
class Element
|
||||
: public Object
|
||||
// , public RuntimeComposer
|
||||
, public RuntimeComposer<Element>
|
||||
, public ClickHandlerHost {
|
||||
public:
|
||||
Element(not_null<HistoryItem*> data, Context context);
|
||||
|
@ -35,12 +58,15 @@ public:
|
|||
NeedsResize = 0x01,
|
||||
AttachedToPrevious = 0x02,
|
||||
AttachedToNext = 0x04,
|
||||
HiddenByGroup = 0x08,
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
|
||||
not_null<HistoryItem*> data() const;
|
||||
HistoryMedia *media() const;
|
||||
Context context() const;
|
||||
void refreshDataId();
|
||||
|
||||
int y() const;
|
||||
void setY(int y);
|
||||
|
@ -49,10 +75,23 @@ public:
|
|||
int marginBottom() const;
|
||||
void setPendingResize();
|
||||
bool pendingResize() const;
|
||||
bool isUnderCursor() const;
|
||||
|
||||
bool isAttachedToPrevious() 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().
|
||||
void setAttachToPrevious(bool attachToNext);
|
||||
|
||||
|
@ -75,6 +114,23 @@ public:
|
|||
QPoint point,
|
||||
HistoryStateRequest request) const = 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.
|
||||
void clickHandlerActiveChanged(
|
||||
|
@ -86,10 +142,25 @@ public:
|
|||
|
||||
// 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
|
||||
virtual bool displayFromPhoto() const;
|
||||
virtual bool hasFromPhoto() const;
|
||||
virtual bool displayFromPhoto() const;
|
||||
virtual bool hasFromName() 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.
|
||||
HistoryBlock *block();
|
||||
|
@ -126,7 +197,12 @@ private:
|
|||
virtual QSize performCountOptimalSize() = 0;
|
||||
virtual QSize performCountCurrentSize(int newWidth) = 0;
|
||||
|
||||
void initGroup();
|
||||
void resetGroupMedia(const std::vector<not_null<Element*>> &others);
|
||||
|
||||
const not_null<HistoryItem*> _data;
|
||||
std::unique_ptr<HistoryMedia> _media;
|
||||
|
||||
int _y = 0;
|
||||
Context _context;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "boxes/edit_participant_box.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_feed.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "styles/style_history.h"
|
||||
|
||||
namespace HistoryView {
|
||||
|
@ -655,7 +656,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
|||
|
||||
TextWithEntities ListWidget::getSelectedText() const {
|
||||
return _selectedItem
|
||||
? _selectedItem->data()->selectedText(_selectedText)
|
||||
? _selectedItem->selectedText(_selectedText)
|
||||
: TextWithEntities();
|
||||
}
|
||||
|
||||
|
@ -825,7 +826,7 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
const auto item = view ? view->data().get() : nullptr;
|
||||
const auto itemId = item ? item->fullId() : FullMsgId();
|
||||
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);
|
||||
if (isUponSelected > 0) {
|
||||
|
@ -833,7 +834,7 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
} else {
|
||||
if (item && !isUponSelected) {
|
||||
auto mediaHasTextForCopy = false;
|
||||
if (auto media = (msg ? msg->getMedia() : nullptr)) {
|
||||
if (auto media = view->media()) {
|
||||
mediaHasTextForCopy = media->hasTextForCopy();
|
||||
if (media->type() == MediaTypeWebPage && 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) {
|
||||
if (const auto item = App::histItemById(itemId)) {
|
||||
if (auto media = item->getMedia()) {
|
||||
if (auto document = media->getDocument()) {
|
||||
if (auto media = item->media()) {
|
||||
if (auto document = media->document()) {
|
||||
Messenger::Instance().showDocument(document, item);
|
||||
}
|
||||
}
|
||||
|
@ -964,12 +965,9 @@ void ListWidget::openContextGif(FullMsgId itemId) {
|
|||
|
||||
void ListWidget::copyContextText(FullMsgId itemId) {
|
||||
if (const auto item = App::histItemById(itemId)) {
|
||||
if (const auto media = item->getMedia()) {
|
||||
if (media->type() == MediaTypeSticker) {
|
||||
return;
|
||||
}
|
||||
if (const auto view = viewForItem(item)) {
|
||||
setToClipboard(view->selectedText(FullSelection));
|
||||
}
|
||||
setToClipboard(item->selectedText(FullSelection));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1243,7 +1241,7 @@ void ListWidget::updateSelected() {
|
|||
}
|
||||
auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) };
|
||||
if (_mouseSelectType != TextSelectType::Letters) {
|
||||
selection = _mouseActionItem->data()->adjustSelection(selection, _mouseSelectType);
|
||||
selection = _mouseActionItem->adjustSelection(selection, _mouseSelectType);
|
||||
}
|
||||
if (_selectedText != selection) {
|
||||
_selectedText = selection;
|
||||
|
@ -1342,16 +1340,20 @@ void ListWidget::performDrag() {
|
|||
// auto forwardMimeType = QString();
|
||||
// auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
|
||||
// if (auto pressedItem = App::pressedItem()) {
|
||||
// pressedMedia = pressedItem->getMedia();
|
||||
// if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) {
|
||||
// Auth().data().setMimeForwardIds(Auth().data().itemOrItsGroup(pressedItem));
|
||||
// pressedMedia = pressedItem->media();
|
||||
// if (_mouseCursorState == HistoryInDateCursorState
|
||||
// || (pressedMedia && pressedMedia->dragItem())) {
|
||||
// Auth().data().setMimeForwardIds(
|
||||
// Auth().data().itemOrItsGroup(pressedItem->data()));
|
||||
// forwardMimeType = qsl("application/x-td-forward");
|
||||
// }
|
||||
// }
|
||||
// if (auto pressedLnkItem = App::pressedLinkItem()) {
|
||||
// if ((pressedMedia = pressedLnkItem->getMedia())) {
|
||||
// if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) {
|
||||
// Auth().data().setMimeForwardIds({ 1, pressedLnkItem->fullId() });
|
||||
// if ((pressedMedia = pressedLnkItem->media())) {
|
||||
// if (forwardMimeType.isEmpty()
|
||||
// && pressedMedia->dragItemByHandler(pressedHandler)) {
|
||||
// Auth().data().setMimeForwardIds(
|
||||
// { 1, pressedLnkItem->fullId() });
|
||||
// forwardMimeType = qsl("application/x-td-forward");
|
||||
// }
|
||||
// }
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,9 +10,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_element.h"
|
||||
|
||||
class HistoryMessage;
|
||||
struct HistoryMessageEdited;
|
||||
|
||||
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 {
|
||||
public:
|
||||
Message(not_null<HistoryMessage*> data, Context context);
|
||||
|
@ -27,20 +40,58 @@ public:
|
|||
QPoint point,
|
||||
HistoryStateRequest request) const 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
|
||||
// but we need to skip a place at the left side for this photo
|
||||
bool displayFromPhoto() const override;
|
||||
bool hasFromPhoto() const override;
|
||||
|
||||
bool displayFromPhoto() const override;
|
||||
bool hasFromName() 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:
|
||||
not_null<HistoryMessage*> message() const;
|
||||
|
||||
void initLogEntryOriginal();
|
||||
void refreshEditedBadge();
|
||||
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 paintForwardedInfo(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 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
|
||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_abstract_structure.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "mainwidget.h"
|
||||
#include "layout.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
||||
namespace HistoryView {
|
||||
|
@ -309,7 +310,7 @@ QRect Service::countGeometry() const {
|
|||
|
||||
QSize Service::performCountCurrentSize(int newWidth) {
|
||||
const auto item = message();
|
||||
const auto media = item->getMedia();
|
||||
const auto media = this->media();
|
||||
|
||||
auto newHeight = item->displayedDateHeight();
|
||||
if (auto unreadbar = item->Get<HistoryMessageUnreadBar>()) {
|
||||
|
@ -349,7 +350,7 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
|||
|
||||
QSize Service::performCountOptimalSize() {
|
||||
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 minHeight = item->_text.minHeight();
|
||||
|
@ -409,11 +410,11 @@ void Service::draw(
|
|||
|
||||
p.setTextPalette(st::serviceTextPalette);
|
||||
|
||||
if (auto media = item->getMedia()) {
|
||||
if (auto media = this->media()) {
|
||||
height -= st::msgServiceMargin.top() + media->height();
|
||||
auto left = st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.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);
|
||||
}
|
||||
|
||||
|
@ -435,7 +436,7 @@ void Service::draw(
|
|||
|
||||
bool Service::hasPoint(QPoint point) const {
|
||||
const auto item = message();
|
||||
const auto media = item->getMedia();
|
||||
const auto media = this->media();
|
||||
|
||||
auto g = countGeometry();
|
||||
if (g.width() < 1) {
|
||||
|
@ -456,7 +457,7 @@ bool Service::hasPoint(QPoint point) const {
|
|||
|
||||
HistoryTextState Service::getState(QPoint point, HistoryStateRequest request) const {
|
||||
const auto item = message();
|
||||
const auto media = item->getMedia();
|
||||
const auto media = this->media();
|
||||
|
||||
auto result = HistoryTextState(item);
|
||||
|
||||
|
@ -504,4 +505,15 @@ HistoryTextState Service::getState(QPoint point, HistoryStateRequest request) co
|
|||
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
|
||||
|
|
|
@ -27,6 +27,10 @@ public:
|
|||
QPoint point,
|
||||
HistoryStateRequest request) const override;
|
||||
void updatePressed(QPoint point) override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
TextSelection adjustSelection(
|
||||
TextSelection selection,
|
||||
TextSelectType type) const override;
|
||||
|
||||
private:
|
||||
not_null<HistoryService*> message() const;
|
||||
|
|
|
@ -9,7 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "info/info_controller.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.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/confirm_box.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "data/data_session.h"
|
||||
|
||||
namespace Layout = Overview::Layout;
|
||||
|
||||
|
@ -837,14 +839,14 @@ std::unique_ptr<BaseLayout> ListWidget::createLayout(
|
|||
return nullptr;
|
||||
}
|
||||
auto getPhoto = [&]() -> PhotoData* {
|
||||
if (const auto media = item->getMedia()) {
|
||||
return media->getPhoto();
|
||||
if (const auto media = item->media()) {
|
||||
return media->photo();
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
auto getFile = [&]() -> DocumentData* {
|
||||
if (auto media = item->getMedia()) {
|
||||
return media->getDocument();
|
||||
if (auto media = item->media()) {
|
||||
return media->document();
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
@ -878,7 +880,7 @@ std::unique_ptr<BaseLayout> ListWidget::createLayout(
|
|||
}
|
||||
return nullptr;
|
||||
case Type::Link:
|
||||
return std::make_unique<Link>(item, item->getMedia());
|
||||
return std::make_unique<Link>(item, item->media());
|
||||
case Type::RoundFile:
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1313,7 +1315,7 @@ void ListWidget::showContextMenu(
|
|||
}));
|
||||
} else {
|
||||
if (overSelected != SelectionState::NotOverSelectedItems) {
|
||||
if (item->canForward()) {
|
||||
if (item->allowsForward()) {
|
||||
_contextMenu->addAction(
|
||||
lang(lng_context_forward_msg),
|
||||
base::lambda_guarded(this, [this, universalId] {
|
||||
|
@ -1497,7 +1499,7 @@ bool ListWidget::changeItemSelection(
|
|||
return false;
|
||||
}
|
||||
iterator->second.canDelete = item->canDelete();
|
||||
iterator->second.canForward = item->canForward();
|
||||
iterator->second.canForward = item->allowsForward();
|
||||
return true;
|
||||
}
|
||||
return changeExisting(iterator);
|
||||
|
|
|
@ -78,7 +78,9 @@ public:
|
|||
|
||||
};
|
||||
|
||||
class LayoutItemBase : public RuntimeComposer, public ClickHandlerHost {
|
||||
class LayoutItemBase
|
||||
: public RuntimeComposer<LayoutItemBase>
|
||||
, public ClickHandlerHost {
|
||||
public:
|
||||
LayoutItemBase() {
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_peer_values.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
|
@ -327,8 +328,8 @@ void MainWidget::checkCurrentFloatPlayer() {
|
|||
last->widget->detach();
|
||||
}
|
||||
if (auto item = App::histItemById(fullId)) {
|
||||
if (auto media = item->getMedia()) {
|
||||
if (auto document = media->getDocument()) {
|
||||
if (auto media = item->media()) {
|
||||
if (auto document = media->document()) {
|
||||
if (document->isVideoMessage()) {
|
||||
_playerFloats.push_back(std::make_unique<Float>(this, item, [this](not_null<Float*> instance, bool visible) {
|
||||
instance->hiddenByWidget = !visible;
|
||||
|
@ -4908,11 +4909,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
|||
if (existing) {
|
||||
existing->destroy();
|
||||
}
|
||||
App::historyUnregItem(local);
|
||||
Auth().messageIdChanging.notify({ local, newId }, true);
|
||||
local->setRealId(d.vid.v);
|
||||
App::historyRegItem(local);
|
||||
Auth().data().requestItemRepaint(local);
|
||||
}
|
||||
}
|
||||
App::historyUnregRandom(d.vrandom_id.v);
|
||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <rpl/merge.h>
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "history/history_media.h"
|
||||
#include "history/history_item.h"
|
||||
#include "media/media_clip_reader.h"
|
||||
|
@ -31,10 +32,10 @@ Float::Float(
|
|||
, _item(item)
|
||||
, _toggleCallback(std::move(toggleCallback))
|
||||
, _draggedCallback(std::move(draggedCallback)) {
|
||||
auto media = _item->getMedia();
|
||||
auto media = _item->media();
|
||||
Assert(media != nullptr);
|
||||
|
||||
auto document = media->getDocument();
|
||||
auto document = media->document();
|
||||
Assert(document != nullptr);
|
||||
Assert(document->isVideoMessage());
|
||||
|
||||
|
@ -100,9 +101,10 @@ float64 Float::outRatio() const {
|
|||
void Float::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (_down) {
|
||||
_down = false;
|
||||
if (auto media = _item ? _item->getMedia() : nullptr) {
|
||||
media->playInline();
|
||||
}
|
||||
// #TODO float player
|
||||
//if (auto media = _item ? _item->getMedia() : nullptr) {
|
||||
// media->playInline();
|
||||
//}
|
||||
}
|
||||
if (_drag) {
|
||||
finishDrag(outRatio() < 0.5);
|
||||
|
@ -119,9 +121,10 @@ void Float::finishDrag(bool closed) {
|
|||
void Float::mouseDoubleClickEvent(QMouseEvent *e) {
|
||||
if (_item) {
|
||||
// Handle second click.
|
||||
if (auto media = _item->getMedia()) {
|
||||
media->playInline();
|
||||
}
|
||||
// #TODO float player
|
||||
//if (auto media = _item->getMedia()) {
|
||||
// media->playInline();
|
||||
//}
|
||||
Ui::showPeerHistoryAtItem(_item);
|
||||
}
|
||||
}
|
||||
|
@ -191,13 +194,14 @@ void Float::paintEvent(QPaintEvent *e) {
|
|||
}
|
||||
|
||||
Clip::Reader *Float::getReader() const {
|
||||
if (auto media = _item ? _item->getMedia() : nullptr) {
|
||||
if (auto reader = media->getClipReader()) {
|
||||
if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) {
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
}
|
||||
// #TODO float player
|
||||
//if (auto media = _item ? _item->getMedia() : nullptr) {
|
||||
// if (auto reader = media->getClipReader()) {
|
||||
// if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) {
|
||||
// return reader;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "calls/calls_instance.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_media.h"
|
||||
#include "data/data_media_types.h"
|
||||
|
||||
namespace Media {
|
||||
namespace Player {
|
||||
|
@ -222,8 +222,8 @@ bool Instance::moveInPlaylist(
|
|||
}
|
||||
const auto newIndex = *data->playlistIndex + delta;
|
||||
if (const auto item = itemByIndex(data, newIndex)) {
|
||||
if (const auto media = item->getMedia()) {
|
||||
if (const auto document = media->getDocument()) {
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto document = media->document()) {
|
||||
if (autonext) {
|
||||
_switchToNextNotifier.notify({
|
||||
data->current,
|
||||
|
@ -300,11 +300,12 @@ void Instance::play(const AudioMsgId &audioId) {
|
|||
documentLoadProgress(document);
|
||||
}
|
||||
} else if (document->isVideoMessage()) {
|
||||
if (const auto item = App::histItemById(audioId.contextId())) {
|
||||
if (const auto media = item->getMedia()) {
|
||||
media->playInline();
|
||||
}
|
||||
}
|
||||
// #TODO float player
|
||||
//if (const auto item = App::histItemById(audioId.contextId())) {
|
||||
// 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;
|
||||
if (const auto item = itemByIndex(data, nextIndex)) {
|
||||
if (const auto media = item->getMedia()) {
|
||||
if (const auto document = media->getDocument()) {
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto document = media->document()) {
|
||||
const auto isLoaded = document->loaded(
|
||||
DocumentData::FilePathResolveSaveFromDataSilent);
|
||||
if (!isLoaded) {
|
||||
|
|
|
@ -12,8 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "info/media/info_media_list_widget.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_media.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "mainwindow.h"
|
||||
|
@ -258,8 +258,8 @@ void Panel::refreshList() {
|
|||
const auto contextId = current.contextId();
|
||||
const auto peer = [&]() -> PeerData* {
|
||||
const auto item = contextId ? App::histItemById(contextId) : nullptr;
|
||||
const auto media = item ? item->getMedia() : nullptr;
|
||||
const auto document = media ? media->getDocument() : nullptr;
|
||||
const auto media = item ? item->media() : nullptr;
|
||||
const auto document = media ? media->document() : nullptr;
|
||||
if (!document || !document->isSharedMediaMusic()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_user_photos.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_media.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());
|
||||
} else if (const auto msgId = base::get_if<FullMsgId>(&key)) {
|
||||
if (const auto item = App::histItemById(*msgId)) {
|
||||
if (const auto media = item->getMedia()) {
|
||||
if (const auto photo = media->getPhoto()) {
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto photo = media->photo()) {
|
||||
return createThumb(key, photo->thumb);
|
||||
} else if (const auto document = media->getDocument()) {
|
||||
} else if (const auto document = media->document()) {
|
||||
return createThumb(key, document->thumb);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "window/themes/window_theme_preview.h"
|
||||
#include "window/window_peer_menu.h"
|
||||
#include "observer_peer.h"
|
||||
|
@ -1236,13 +1237,13 @@ void MediaView::refreshMediaViewer() {
|
|||
void MediaView::refreshCaption(HistoryItem *item) {
|
||||
_caption = Text();
|
||||
|
||||
const auto media = item ? item->getMedia() : nullptr;
|
||||
const auto media = item ? item->media() : nullptr;
|
||||
if (!media) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto caption = media->getCaption();
|
||||
if (caption.text.isEmpty()) {
|
||||
const auto caption = media->caption();
|
||||
if (caption.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const auto asBot = [&] {
|
||||
|
@ -1254,7 +1255,7 @@ void MediaView::refreshCaption(HistoryItem *item) {
|
|||
_caption = Text(st::msgMinWidth);
|
||||
_caption.setMarkedText(
|
||||
st::mediaviewCaptionStyle,
|
||||
caption,
|
||||
{ caption, EntitiesInText() }, // #TODO caption entities parse
|
||||
Ui::ItemTextOptions(item));
|
||||
}
|
||||
|
||||
|
@ -2448,10 +2449,10 @@ MediaView::Entity MediaView::entityForSharedMedia(int index) const {
|
|||
|
||||
MediaView::Entity MediaView::entityForItemId(const FullMsgId &itemId) const {
|
||||
if (const auto item = App::histItemById(itemId)) {
|
||||
if (const auto media = item->getMedia()) {
|
||||
if (const auto photo = media->getPhoto()) {
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto photo = media->photo()) {
|
||||
return { photo, item };
|
||||
} else if (const auto document = media->getDocument()) {
|
||||
} else if (const auto document = media->document()) {
|
||||
return { document, item };
|
||||
}
|
||||
}
|
||||
|
@ -2474,7 +2475,7 @@ void MediaView::setContext(base::optional_variant<
|
|||
not_null<PeerData*>> context) {
|
||||
if (auto item = base::get_if<not_null<HistoryItem*>>(&context)) {
|
||||
_msgid = (*item)->fullId();
|
||||
_canForwardItem = (*item)->canForward();
|
||||
_canForwardItem = (*item)->allowsForward();
|
||||
_canDeleteItem = (*item)->canDelete();
|
||||
_history = (*item)->history();
|
||||
_peer = _history->peer;
|
||||
|
|
|
@ -68,8 +68,8 @@ enum class MTPDmessage_ClientFlag : uint32 {
|
|||
// message has an admin badge in supergroup
|
||||
f_has_admin_badge = (1U << 20),
|
||||
|
||||
// message is not displayed because it is part of a group
|
||||
f_hidden_by_group = (1U << 19),
|
||||
//// message is not displayed because it is part of a group
|
||||
//f_hidden_by_group = (1U << 19),
|
||||
|
||||
// update this when adding new client side flags
|
||||
MIN_FIELD = (1U << 19),
|
||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_web_page.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "styles/style_overview.h"
|
||||
#include "styles/style_history.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 "storage/localstorage.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "ui/effects/round_checkbox.h"
|
||||
#include "ui/text_options.h"
|
||||
|
@ -1165,7 +1165,7 @@ bool Document::updateStatusText() {
|
|||
|
||||
Link::Link(
|
||||
not_null<HistoryItem*> parent,
|
||||
HistoryMedia *media)
|
||||
Data::Media *media)
|
||||
: ItemBase(parent) {
|
||||
AddComponents(Info::Bit());
|
||||
|
||||
|
@ -1211,9 +1211,10 @@ Link::Link(
|
|||
}
|
||||
}
|
||||
|
||||
_page = (media && media->type() == MediaTypeWebPage)
|
||||
? static_cast<HistoryWebPage*>(media)->webpage().get()
|
||||
: nullptr;
|
||||
// #TODO webpage
|
||||
//_page = (media && media->type() == MediaTypeWebPage)
|
||||
// ? static_cast<HistoryWebPage*>(media)->webpage().get()
|
||||
// : nullptr;
|
||||
if (_page) {
|
||||
mainUrl = _page->url;
|
||||
if (_page->document) {
|
||||
|
|
|
@ -20,6 +20,10 @@ namespace style {
|
|||
struct RoundCheckbox;
|
||||
} // namespace style
|
||||
|
||||
namespace Data {
|
||||
class Media;
|
||||
} // namespace Data
|
||||
|
||||
namespace Overview {
|
||||
namespace Layout {
|
||||
|
||||
|
@ -172,7 +176,7 @@ private:
|
|||
|
||||
};
|
||||
|
||||
struct Info : public RuntimeComponent<Info> {
|
||||
struct Info : public RuntimeComponent<Info, LayoutItemBase> {
|
||||
int top = 0;
|
||||
};
|
||||
|
||||
|
@ -325,7 +329,7 @@ class Link : public ItemBase {
|
|||
public:
|
||||
Link(
|
||||
not_null<HistoryItem*> parent,
|
||||
HistoryMedia *media);
|
||||
Data::Media *media);
|
||||
|
||||
void initDimensions() override;
|
||||
int32 resizeGetHeight(int32 width) override;
|
||||
|
|
|
@ -2698,24 +2698,35 @@ bool Text::hasSkipBlock() const {
|
|||
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) {
|
||||
auto block = static_cast<SkipBlock*>(_blocks.back().get());
|
||||
if (block->width() == width && block->height() == height) return;
|
||||
const auto block = static_cast<SkipBlock*>(_blocks.back().get());
|
||||
if (block->width() == width && block->height() == height) {
|
||||
return false;
|
||||
}
|
||||
_text.resize(block->from());
|
||||
_blocks.pop_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);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Text::removeSkipBlock() {
|
||||
if (!_blocks.empty() && _blocks.back()->type() == TextBlockTSkip) {
|
||||
_text.resize(_blocks.back()->from());
|
||||
_blocks.pop_back();
|
||||
recountNaturalSize(false);
|
||||
bool Text::removeSkipBlock() {
|
||||
if (_blocks.empty() || _blocks.back()->type() != TextBlockTSkip) {
|
||||
return false;
|
||||
}
|
||||
_text.resize(_blocks.back()->from());
|
||||
_blocks.pop_back();
|
||||
recountNaturalSize(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
int Text::countWidth(int width) const {
|
||||
|
|
|
@ -89,8 +89,8 @@ public:
|
|||
bool hasLinks() const;
|
||||
|
||||
bool hasSkipBlock() const;
|
||||
void setSkipBlock(int32 width, int32 height);
|
||||
void removeSkipBlock();
|
||||
bool updateSkipBlock(int width, int height);
|
||||
bool removeSkipBlock();
|
||||
|
||||
int32 maxWidth() const {
|
||||
return _maxWidth.ceil().toInt();
|
||||
|
|
|
@ -427,13 +427,24 @@ void Manager::notificationReplied(
|
|||
}
|
||||
|
||||
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;
|
||||
QString 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 title = options.hideNameAndPhoto ? qsl("Telegram Desktop") : item->history()->peer->name;
|
||||
const auto subtitle = options.hideNameAndPhoto ? QString() : item->notificationHeader();
|
||||
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;
|
||||
|
|
|
@ -173,6 +173,10 @@
|
|||
<(src_loc)/data/data_feed_messages.h
|
||||
<(src_loc)/data/data_flags.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.h
|
||||
<(src_loc)/data/data_notify_settings.cpp
|
||||
|
|
Loading…
Add table
Reference in a new issue