Fix crash in DocumentData destructor.

Keep AuthSession pointer in DocumentData for loader destruction.
This commit is contained in:
John Preston 2018-01-27 12:21:57 +03:00
parent 63c1212ef1
commit a858ab5d0b
8 changed files with 187 additions and 180 deletions

View File

@ -268,14 +268,14 @@ AuthSession &Auth() {
AuthSession::AuthSession(UserId userId) AuthSession::AuthSession(UserId userId)
: _userId(userId) : _userId(userId)
, _autoLockTimer([this] { checkAutoLock(); }) , _autoLockTimer([this] { checkAutoLock(); })
, _data(std::make_unique<Data::Session>(this))
, _api(std::make_unique<ApiWrap>(this)) , _api(std::make_unique<ApiWrap>(this))
, _calls(std::make_unique<Calls::Instance>()) , _calls(std::make_unique<Calls::Instance>())
, _downloader(std::make_unique<Storage::Downloader>()) , _downloader(std::make_unique<Storage::Downloader>())
, _uploader(std::make_unique<Storage::Uploader>()) , _uploader(std::make_unique<Storage::Uploader>())
, _storage(std::make_unique<Storage::Facade>()) , _storage(std::make_unique<Storage::Facade>())
, _notifications(std::make_unique<Window::Notifications::System>(this)) , _notifications(std::make_unique<Window::Notifications::System>(this))
, _changelogs(Core::Changelogs::Create(this)) { , _changelogs(Core::Changelogs::Create(this))
, _data(std::make_unique<Data::Session>(this)) {
Expects(_userId != 0); Expects(_userId != 0);
_saveDataTimer.setCallback([this] { _saveDataTimer.setCallback([this] {

View File

@ -252,7 +252,6 @@ private:
TimeMs _shouldLockAt = 0; TimeMs _shouldLockAt = 0;
base::Timer _autoLockTimer; base::Timer _autoLockTimer;
const std::unique_ptr<Data::Session> _data;
const std::unique_ptr<ApiWrap> _api; const std::unique_ptr<ApiWrap> _api;
const std::unique_ptr<Calls::Instance> _calls; const std::unique_ptr<Calls::Instance> _calls;
const std::unique_ptr<Storage::Downloader> _downloader; const std::unique_ptr<Storage::Downloader> _downloader;
@ -261,6 +260,9 @@ private:
const std::unique_ptr<Window::Notifications::System> _notifications; const std::unique_ptr<Window::Notifications::System> _notifications;
const std::unique_ptr<Core::Changelogs> _changelogs; const std::unique_ptr<Core::Changelogs> _changelogs;
// _data depends on _downloader / _uploader, including destructor.
const std::unique_ptr<Data::Session> _data;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;
}; };

View File

@ -152,28 +152,6 @@ QString saveFileName(const QString &title, const QString &filter, const QString
return name; return name;
} }
bool StickerData::setInstalled() const {
switch (set.type()) {
case mtpc_inputStickerSetID: {
auto it = Auth().data().stickerSets().constFind(
set.c_inputStickerSetID().vid.v);
return (it != Auth().data().stickerSets().cend())
&& !(it->flags & MTPDstickerSet::Flag::f_archived)
&& (it->flags & MTPDstickerSet::Flag::f_installed_date);
} break;
case mtpc_inputStickerSetShortName: {
auto name = qs(set.c_inputStickerSetShortName().vshort_name).toLower();
for (auto it = Auth().data().stickerSets().cbegin(), e = Auth().data().stickerSets().cend(); it != e; ++it) {
if (it->shortName.toLower() == name) {
return !(it->flags & MTPDstickerSet::Flag::f_archived)
&& (it->flags & MTPDstickerSet::Flag::f_installed_date);
}
}
} break;
}
return false;
}
QString documentSaveFilename(const DocumentData *data, bool forceSavingAs = false, const QString already = QString(), const QDir &dir = QDir()) { QString documentSaveFilename(const DocumentData *data, bool forceSavingAs = false, const QString already = QString(), const QDir &dir = QDir()) {
auto alreadySavingFilename = data->loadingFilePath(); auto alreadySavingFilename = data->loadingFilePath();
if (!alreadySavingFilename.isEmpty()) { if (!alreadySavingFilename.isEmpty()) {
@ -253,9 +231,7 @@ void DocumentOpenClickHandler::doOpen(
auto audio = AudioMsgId(data, msgId); auto audio = AudioMsgId(data, msgId);
Media::Player::mixer()->play(audio); Media::Player::mixer()->play(audio);
Media::Player::Updated().notify(audio); Media::Player::Updated().notify(audio);
if (App::main()) { data->session()->data().markMediaRead(data);
Auth().data().markMediaRead(data);
}
} }
} else if (playMusic) { } else if (playMusic) {
auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song); auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song);
@ -282,24 +258,24 @@ void DocumentOpenClickHandler::doOpen(
File::Launch(filepath); File::Launch(filepath);
} }
} }
Auth().data().markMediaRead(data); data->session()->data().markMediaRead(data);
} else if (data->isVoiceMessage() || data->isAudioFile() || data->isVideoFile()) { } else if (data->isVoiceMessage() || data->isAudioFile() || data->isVideoFile()) {
auto filepath = location.name(); auto filepath = location.name();
if (documentIsValidMediaFile(filepath)) { if (documentIsValidMediaFile(filepath)) {
File::Launch(filepath); File::Launch(filepath);
} }
Auth().data().markMediaRead(data); data->session()->data().markMediaRead(data);
} else if (data->size < App::kImageSizeLimit) { } else if (data->size < App::kImageSizeLimit) {
if (!data->data().isEmpty() && playAnimation) { if (!data->data().isEmpty() && playAnimation) {
if (action == ActionOnLoadPlayInline && context) { if (action == ActionOnLoadPlayInline && context) {
Auth().data().requestAnimationPlayInline(context); data->session()->data().requestAnimationPlayInline(context);
} else { } else {
Messenger::Instance().showDocument(data, context); Messenger::Instance().showDocument(data, context);
} }
} else if (location.accessEnable()) { } else if (location.accessEnable()) {
if (playAnimation || QImageReader(location.name()).canRead()) { if (playAnimation || QImageReader(location.name()).canRead()) {
if (playAnimation && action == ActionOnLoadPlayInline && context) { if (playAnimation && action == ActionOnLoadPlayInline && context) {
Auth().data().requestAnimationPlayInline(context); data->session()->data().requestAnimationPlayInline(context);
} else { } else {
Messenger::Instance().showDocument(data, context); Messenger::Instance().showDocument(data, context);
} }
@ -378,35 +354,22 @@ void DocumentCancelClickHandler::onClickImpl() const {
} }
VoiceData::~VoiceData() { VoiceData::~VoiceData() {
if (!waveform.isEmpty() && waveform.at(0) == -1 && waveform.size() > int32(sizeof(TaskId))) { if (!waveform.isEmpty()
&& waveform[0] == -1
&& waveform.size() > int32(sizeof(TaskId))) {
TaskId taskId = 0; TaskId taskId = 0;
memcpy(&taskId, waveform.constData() + 1, sizeof(taskId)); memcpy(&taskId, waveform.constData() + 1, sizeof(taskId));
Local::cancelTask(taskId); Local::cancelTask(taskId);
} }
} }
DocumentData::DocumentData(DocumentId id, int32 dc, uint64 accessHash, int32 version, const QString &url, const QVector<MTPDocumentAttribute> &attributes) DocumentData::DocumentData(DocumentId id, not_null<AuthSession*> session)
: id(id) : id(id)
, _dc(dc) , _session(session) {
, _access(accessHash)
, _version(version)
, _url(url) {
setattributes(attributes);
if (_dc && _access) {
_location = Local::readFileLocation(mediaKey());
}
} }
DocumentData *DocumentData::create(DocumentId id) { not_null<AuthSession*> DocumentData::session() const {
return new DocumentData(id, 0, 0, 0, QString(), QVector<MTPDocumentAttribute>()); return _session;
}
DocumentData *DocumentData::create(DocumentId id, int32 dc, uint64 accessHash, int32 version, const QVector<MTPDocumentAttribute> &attributes) {
return new DocumentData(id, dc, accessHash, version, QString(), attributes);
}
DocumentData *DocumentData::create(DocumentId id, const QString &url, const QVector<MTPDocumentAttribute> &attributes) {
return new DocumentData(id, 0, 0, 0, url, attributes);
} }
void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes) { void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes) {
@ -577,7 +540,7 @@ void DocumentData::performActionOnLoad() {
} }
} else if (Media::Player::IsStopped(state.state)) { } else if (Media::Player::IsStopped(state.state)) {
Media::Player::mixer()->play(AudioMsgId(this, _actionOnLoadMsgId)); Media::Player::mixer()->play(AudioMsgId(this, _actionOnLoadMsgId));
Auth().data().markMediaRead(this); _session->data().markMediaRead(this);
} }
} }
} else if (playMusic) { } else if (playMusic) {
@ -598,7 +561,7 @@ void DocumentData::performActionOnLoad() {
} else if (playAnimation) { } else if (playAnimation) {
if (loaded()) { if (loaded()) {
if (_actionOnLoad == ActionOnLoadPlayInline && item) { if (_actionOnLoad == ActionOnLoadPlayInline && item) {
Auth().data().requestAnimationPlayInline(item); _session->data().requestAnimationPlayInline(item);
} else { } else {
Messenger::Instance().showDocument(this, item); Messenger::Instance().showDocument(this, item);
} }
@ -613,7 +576,7 @@ void DocumentData::performActionOnLoad() {
if (documentIsValidMediaFile(already)) { if (documentIsValidMediaFile(already)) {
File::Launch(already); File::Launch(already);
} }
Auth().data().markMediaRead(this); _session->data().markMediaRead(this);
} else if (loc.accessEnable()) { } else if (loc.accessEnable()) {
if (showImage && QImageReader(loc.name()).canRead()) { if (showImage && QImageReader(loc.name()).canRead()) {
Messenger::Instance().showDocument(this, item); Messenger::Instance().showDocument(this, item);
@ -642,7 +605,7 @@ bool DocumentData::loaded(FilePathResolveType type) const {
} }
destroyLoaderDelayed(); destroyLoaderDelayed();
} }
Auth().data().notifyDocumentLayoutChanged(this); _session->data().notifyDocumentLayoutChanged(this);
} }
return !data().isEmpty() || !filepath(type).isEmpty(); return !data().isEmpty() || !filepath(type).isEmpty();
} }
@ -650,7 +613,7 @@ bool DocumentData::loaded(FilePathResolveType type) const {
void DocumentData::destroyLoaderDelayed(mtpFileLoader *newValue) const { void DocumentData::destroyLoaderDelayed(mtpFileLoader *newValue) const {
_loader->stop(); _loader->stop();
auto loader = std::unique_ptr<FileLoader>(std::exchange(_loader, newValue)); auto loader = std::unique_ptr<FileLoader>(std::exchange(_loader, newValue));
Auth().downloader().delayedDestroyLoader(std::move(loader)); _session->downloader().delayedDestroyLoader(std::move(loader));
} }
bool DocumentData::loading() const { bool DocumentData::loading() const {
@ -752,7 +715,7 @@ void DocumentData::save(
_loader->connect(_loader, SIGNAL(failed(FileLoader*,bool)), App::main(), SLOT(documentLoadFailed(FileLoader*,bool))); _loader->connect(_loader, SIGNAL(failed(FileLoader*,bool)), App::main(), SLOT(documentLoadFailed(FileLoader*,bool)));
_loader->start(); _loader->start();
} }
Auth().data().notifyDocumentLayoutChanged(this); _session->data().notifyDocumentLayoutChanged(this);
} }
void DocumentData::cancel() { void DocumentData::cancel() {
@ -763,9 +726,8 @@ void DocumentData::cancel() {
auto loader = std::unique_ptr<FileLoader>(std::exchange(_loader, CancelledMtpFileLoader)); auto loader = std::unique_ptr<FileLoader>(std::exchange(_loader, CancelledMtpFileLoader));
loader->cancel(); loader->cancel();
loader->stop(); loader->stop();
Auth().downloader().delayedDestroyLoader(std::move(loader)); _session->downloader().delayedDestroyLoader(std::move(loader));
_session->data().notifyDocumentLayoutChanged(this);
Auth().data().notifyDocumentLayoutChanged(this);
if (auto main = App::main()) { if (auto main = App::main()) {
main->documentLoadProgress(this); main->documentLoadProgress(this);
} }
@ -870,6 +832,31 @@ QString DocumentData::filepath(FilePathResolveType type, bool forceSavingAs) con
return result; return result;
} }
bool DocumentData::isStickerSetInstalled() const {
Expects(sticker() != nullptr);
const auto &set = sticker()->set;
const auto &sets = _session->data().stickerSets();
switch (set.type()) {
case mtpc_inputStickerSetID: {
auto it = sets.constFind(set.c_inputStickerSetID().vid.v);
return (it != sets.cend())
&& !(it->flags & MTPDstickerSet::Flag::f_archived)
&& (it->flags & MTPDstickerSet::Flag::f_installed_date);
} break;
case mtpc_inputStickerSetShortName: {
auto name = qs(set.c_inputStickerSetShortName().vshort_name).toLower();
for (auto it = sets.cbegin(), e = sets.cend(); it != e; ++it) {
if (it->shortName.toLower() == name) {
return !(it->flags & MTPDstickerSet::Flag::f_archived)
&& (it->flags & MTPDstickerSet::Flag::f_installed_date);
}
}
} break;
}
return false;
}
ImagePtr DocumentData::makeReplyPreview() { ImagePtr DocumentData::makeReplyPreview() {
if (replyPreview->isNull() && !thumb->isNull()) { if (replyPreview->isNull() && !thumb->isNull()) {
if (thumb->loaded()) { if (thumb->loaded()) {
@ -889,6 +876,105 @@ ImagePtr DocumentData::makeReplyPreview() {
return replyPreview; return replyPreview;
} }
StickerData *DocumentData::sticker() const {
return (type == StickerDocument)
? static_cast<StickerData*>(_additional.get())
: nullptr;
}
void DocumentData::checkSticker() {
const auto data = sticker();
if (!data) return;
automaticLoad(nullptr);
if (data->img->isNull() && loaded()) {
if (_data.isEmpty()) {
const FileLocation &loc(location(true));
if (loc.accessEnable()) {
data->img = ImagePtr(loc.name());
loc.accessDisable();
}
} else {
data->img = ImagePtr(_data);
}
}
}
SongData *DocumentData::song() {
return isSong()
? static_cast<SongData*>(_additional.get())
: nullptr;
}
const SongData *DocumentData::song() const {
return const_cast<DocumentData*>(this)->song();
}
VoiceData *DocumentData::voice() {
return isVoiceMessage()
? static_cast<VoiceData*>(_additional.get())
: nullptr;
}
const VoiceData *DocumentData::voice() const {
return const_cast<DocumentData*>(this)->voice();
}
bool DocumentData::hasRemoteLocation() const {
return (_dc != 0 && _access != 0);
}
bool DocumentData::isValid() const {
return hasRemoteLocation() || !_url.isEmpty();
}
MTPInputDocument DocumentData::mtpInput() const {
if (_access) {
return MTP_inputDocument(
MTP_long(id),
MTP_long(_access));
}
return MTP_inputDocumentEmpty();
}
QString DocumentData::filename() const {
return _filename;
}
QString DocumentData::mimeString() const {
return _mimeString;
}
bool DocumentData::hasMimeType(QLatin1String mime) const {
return !_mimeString.compare(mime, Qt::CaseInsensitive);
}
void DocumentData::setMimeString(const QString &mime) {
_mimeString = mime;
}
MediaKey DocumentData::mediaKey() const {
return ::mediaKey(locationType(), _dc, id, _version);
}
QString DocumentData::composeNameString() const {
if (auto songData = song()) {
return ComposeNameString(
_filename,
songData->title,
songData->performer);
}
return ComposeNameString(_filename, QString(), QString());
}
LocationType DocumentData::locationType() const {
return isVoiceMessage()
? AudioFileLocation
: isVideoFile()
? VideoFileLocation
: DocumentFileLocation;
}
bool DocumentData::isVoiceMessage() const { bool DocumentData::isVoiceMessage() const {
return (type == VoiceDocument); return (type == VoiceDocument);
} }

View File

@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_types.h" #include "data/data_types.h"
class AuthSession;
inline uint64 mediaMix32To64(int32 a, int32 b) { inline uint64 mediaMix32To64(int32 a, int32 b) {
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32) return (uint64(*reinterpret_cast<uint32*>(&a)) << 32)
| uint64(*reinterpret_cast<uint32*>(&b)); | uint64(*reinterpret_cast<uint32*>(&b));
@ -42,10 +44,7 @@ struct DocumentAdditionalData {
struct StickerData : public DocumentAdditionalData { struct StickerData : public DocumentAdditionalData {
ImagePtr img; ImagePtr img;
QString alt; QString alt;
MTPInputStickerSet set = MTP_inputStickerSetEmpty(); MTPInputStickerSet set = MTP_inputStickerSetEmpty();
bool setInstalled() const;
StorageImageLocation loc; // doc thumb location StorageImageLocation loc; // doc thumb location
}; };
@ -72,17 +71,9 @@ class Document;
class DocumentData { class DocumentData {
public: public:
static DocumentData *create(DocumentId id); DocumentData(DocumentId id, not_null<AuthSession*> session);
static DocumentData *create(
DocumentId id, not_null<AuthSession*> session() const;
int32 dc,
uint64 accessHash,
int32 version,
const QVector<MTPDocumentAttribute> &attributes);
static DocumentData *create(
DocumentId id,
const QString &url,
const QVector<MTPDocumentAttribute> &attributes);
void setattributes( void setattributes(
const QVector<MTPDocumentAttribute> &attributes); const QVector<MTPDocumentAttribute> &attributes);
@ -130,44 +121,14 @@ public:
void forget(); void forget();
ImagePtr makeReplyPreview(); ImagePtr makeReplyPreview();
StickerData *sticker() { StickerData *sticker() const;
return (type == StickerDocument) void checkSticker();
? static_cast<StickerData*>(_additional.get()) bool isStickerSetInstalled() const;
: nullptr; SongData *song();
} const SongData *song() const;
void checkSticker() { VoiceData *voice();
StickerData *s = sticker(); const VoiceData *voice() const;
if (!s) return;
automaticLoad(nullptr);
if (s->img->isNull() && loaded()) {
if (_data.isEmpty()) {
const FileLocation &loc(location(true));
if (loc.accessEnable()) {
s->img = ImagePtr(loc.name());
loc.accessDisable();
}
} else {
s->img = ImagePtr(_data);
}
}
}
SongData *song() {
return isSong()
? static_cast<SongData*>(_additional.get())
: nullptr;
}
const SongData *song() const {
return const_cast<DocumentData*>(this)->song();
}
VoiceData *voice() {
return isVoiceMessage()
? static_cast<VoiceData*>(_additional.get())
: nullptr;
}
const VoiceData *voice() const {
return const_cast<DocumentData*>(this)->voice();
}
bool isVoiceMessage() const; bool isVoiceMessage() const;
bool isVideoMessage() const; bool isVideoMessage() const;
bool isSong() const; bool isSong() const;
@ -187,20 +148,9 @@ public:
bool setRemoteVersion(int32 version); // Returns true if version has changed. bool setRemoteVersion(int32 version); // Returns true if version has changed.
void setRemoteLocation(int32 dc, uint64 access); void setRemoteLocation(int32 dc, uint64 access);
void setContentUrl(const QString &url); void setContentUrl(const QString &url);
bool hasRemoteLocation() const { bool hasRemoteLocation() const;
return (_dc != 0 && _access != 0); bool isValid() const;
} MTPInputDocument mtpInput() const;
bool isValid() const {
return hasRemoteLocation() || !_url.isEmpty();
}
MTPInputDocument mtpInput() const {
if (_access) {
return MTP_inputDocument(
MTP_long(id),
MTP_long(_access));
}
return MTP_inputDocumentEmpty();
}
// When we have some client-side generated document // When we have some client-side generated document
// (for example for displaying an external inline bot result) // (for example for displaying an external inline bot result)
@ -208,18 +158,18 @@ public:
// to (this) received from the server "same" document. // to (this) received from the server "same" document.
void collectLocalData(DocumentData *local); void collectLocalData(DocumentData *local);
QString filename() const { QString filename() const;
return _filename; QString mimeString() const;
} bool hasMimeType(QLatin1String mime) const;
QString mimeString() const { void setMimeString(const QString &mime);
return _mimeString;
} MediaKey mediaKey() const;
bool hasMimeType(QLatin1String mime) const {
return !_mimeString.compare(mime, Qt::CaseInsensitive); static QString ComposeNameString(
} const QString &filename,
void setMimeString(const QString &mime) { const QString &songTitle,
_mimeString = mime; const QString &songPerformer);
} QString composeNameString() const;
~DocumentData(); ~DocumentData();
@ -234,44 +184,12 @@ public:
std::unique_ptr<Data::UploadState> uploadingData; std::unique_ptr<Data::UploadState> uploadingData;
int32 md5[8];
MediaKey mediaKey() const {
return ::mediaKey(locationType(), _dc, id, _version);
}
static QString ComposeNameString(
const QString &filename,
const QString &songTitle,
const QString &songPerformer);
QString composeNameString() const {
if (auto songData = song()) {
return ComposeNameString(
_filename,
songData->title,
songData->performer);
}
return ComposeNameString(_filename, QString(), QString());
}
private: private:
DocumentData(
DocumentId id,
int32 dc,
uint64 accessHash,
int32 version,
const QString &url,
const QVector<MTPDocumentAttribute> &attributes);
friend class Serialize::Document; friend class Serialize::Document;
LocationType locationType() const { LocationType locationType() const;
return isVoiceMessage()
? AudioFileLocation void destroyLoaderDelayed(mtpFileLoader *newValue = nullptr) const;
: isVideoFile()
? VideoFileLocation
: DocumentFileLocation;
}
// Two types of location: from MTProto by dc+access+version or from web by url // Two types of location: from MTProto by dc+access+version or from web by url
int32 _dc = 0; int32 _dc = 0;
@ -281,6 +199,8 @@ private:
QString _filename; QString _filename;
QString _mimeString; QString _mimeString;
not_null<AuthSession*> _session;
FileLocation _location; FileLocation _location;
QByteArray _data; QByteArray _data;
std::unique_ptr<DocumentAdditionalData> _additional; std::unique_ptr<DocumentAdditionalData> _additional;
@ -290,9 +210,6 @@ private:
FullMsgId _actionOnLoadMsgId; FullMsgId _actionOnLoadMsgId;
mutable FileLoader *_loader = nullptr; mutable FileLoader *_loader = nullptr;
void destroyLoaderDelayed(
mtpFileLoader *newValue = nullptr) const;
}; };
VoiceWaveform documentWaveformDecode(const QByteArray &encoded5bit); VoiceWaveform documentWaveformDecode(const QByteArray &encoded5bit);

View File

@ -699,7 +699,9 @@ void Session::photoApplyFields(
not_null<DocumentData*> Session::document(DocumentId id) { not_null<DocumentData*> Session::document(DocumentId id) {
auto i = _documents.find(id); auto i = _documents.find(id);
if (i == _documents.cend()) { if (i == _documents.cend()) {
i = _documents.emplace(id, DocumentData::create(id)).first; i = _documents.emplace(
id,
std::make_unique<DocumentData>(id, _session)).first;
} }
return i->second.get(); return i->second.get();
} }

View File

@ -1004,7 +1004,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (media->type() == MediaTypeSticker) { if (media->type() == MediaTypeSticker) {
if (const auto document = media->getDocument()) { if (const auto document = media->getDocument()) {
if (document->sticker() && document->sticker()->set.type() != mtpc_inputStickerSetEmpty) { if (document->sticker() && document->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
_menu->addAction(lang(document->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] { _menu->addAction(lang(document->isStickerSetInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] {
showStickerPackInfo(document); showStickerPackInfo(document);
}); });
} }

View File

@ -1517,7 +1517,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (media->type() == MediaTypeSticker) { if (media->type() == MediaTypeSticker) {
if (auto document = media->getDocument()) { if (auto document = media->getDocument()) {
if (document->sticker() && document->sticker()->set.type() != mtpc_inputStickerSetEmpty) { if (document->sticker() && document->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
_menu->addAction(lang(document->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] { _menu->addAction(lang(document->isStickerSetInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] {
showStickerPackInfo(document); showStickerPackInfo(document);
}); });
_menu->addAction(lang(Stickers::IsFaved(document) ? lng_faved_stickers_remove : lng_faved_stickers_add), [=] { _menu->addAction(lang(Stickers::IsFaved(document) ? lng_faved_stickers_remove : lng_faved_stickers_add), [=] {

View File

@ -204,7 +204,7 @@ base::unique_qptr<Ui::PopupMenu> FillContextMenu(
if (media->type() == MediaTypeSticker) { if (media->type() == MediaTypeSticker) {
if (auto document = media->getDocument()) { if (auto document = media->getDocument()) {
if (document->sticker() && document->sticker()->set.type() != mtpc_inputStickerSetEmpty) { if (document->sticker() && document->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
result->addAction(lang(document->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] { result->addAction(lang(document->isStickerSetInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] {
ShowStickerPackInfo(document); ShowStickerPackInfo(document);
}); });
} }