mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Display shared stories in message history.
This commit is contained in:
parent
d7186e68e2
commit
c133f4de69
13 changed files with 229 additions and 43 deletions
|
@ -1503,6 +1503,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_action_pinned_media_sticker" = "a sticker";
|
||||
"lng_action_pinned_media_emoji_sticker" = "a {emoji} sticker";
|
||||
"lng_action_pinned_media_game" = "the game «{game}»";
|
||||
"lng_action_pinned_media_story" = "a story";
|
||||
"lng_action_game_score#one" = "{from} scored {count} in {game}";
|
||||
"lng_action_game_score#other" = "{from} scored {count} in {game}";
|
||||
"lng_action_game_you_scored#one" = "You scored {count} in {game}";
|
||||
|
@ -3810,6 +3811,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_stories_archive_button" = "Archive";
|
||||
"lng_stories_archive_title" = "Stories Archive";
|
||||
|
||||
"lng_stories_link_invalid" = "This link is broken or has expired.";
|
||||
|
||||
// Wnd specific
|
||||
|
||||
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
||||
|
|
|
@ -56,6 +56,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_poll.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "core/application.h"
|
||||
|
@ -72,6 +73,7 @@ namespace {
|
|||
|
||||
constexpr auto kFastRevokeRestriction = 24 * 60 * TimeId(60);
|
||||
constexpr auto kMaxPreviewImages = 3;
|
||||
constexpr auto kLoadingStoryPhotoId = PhotoId(0x7FFF'DEAD'FFFF'FFFFULL);
|
||||
|
||||
using ItemPreview = HistoryView::ItemPreview;
|
||||
using ItemPreviewImage = HistoryView::ItemPreviewImage;
|
||||
|
@ -404,6 +406,10 @@ const WallPaper *Media::paper() const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
FullStoryId Media::storyId() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Media::uploading() const {
|
||||
return false;
|
||||
}
|
||||
|
@ -1968,4 +1974,82 @@ std::unique_ptr<HistoryView::Media> MediaWallPaper::createView(
|
|||
std::make_unique<HistoryView::ThemeDocumentBox>(message, _paper));
|
||||
}
|
||||
|
||||
MediaStory::MediaStory(not_null<HistoryItem*> parent, FullStoryId storyId)
|
||||
: Media(parent)
|
||||
, _storyId(storyId) {
|
||||
const auto stories = &parent->history()->owner().stories();
|
||||
if (!stories->lookup(storyId)) {
|
||||
stories->resolve(storyId, crl::guard(this, [=] {
|
||||
if (stories->lookup(storyId)) {
|
||||
parent->history()->owner().requestItemViewRefresh(parent);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Media> MediaStory::clone(not_null<HistoryItem*> parent) {
|
||||
return std::make_unique<MediaStory>(parent, _storyId);
|
||||
}
|
||||
|
||||
FullStoryId MediaStory::storyId() const {
|
||||
return _storyId;
|
||||
}
|
||||
|
||||
TextWithEntities MediaStory::notificationText() const {
|
||||
const auto stories = &parent()->history()->owner().stories();
|
||||
const auto maybeStory = stories->lookup(_storyId);
|
||||
return WithCaptionNotificationText(
|
||||
tr::lng_in_dlg_story(tr::now),
|
||||
(maybeStory
|
||||
? (*maybeStory)->caption()
|
||||
: TextWithEntities()));
|
||||
}
|
||||
|
||||
QString MediaStory::pinnedTextSubstring() const {
|
||||
return tr::lng_action_pinned_media_story(tr::now);
|
||||
}
|
||||
|
||||
TextForMimeData MediaStory::clipboardText() const {
|
||||
return WithCaptionClipboardText(
|
||||
tr::lng_in_dlg_story(tr::now),
|
||||
parent()->clipboardText());
|
||||
}
|
||||
|
||||
bool MediaStory::updateInlineResultMedia(const MTPMessageMedia &media) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MediaStory::updateSentMedia(const MTPMessageMedia &media) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<HistoryView::Media> MediaStory::createView(
|
||||
not_null<HistoryView::Element*> message,
|
||||
not_null<HistoryItem*> realParent,
|
||||
HistoryView::Element *replacing) {
|
||||
const auto spoiler = false;
|
||||
const auto stories = &parent()->history()->owner().stories();
|
||||
const auto maybeStory = stories->lookup(_storyId);
|
||||
if (const auto story = maybeStory ? maybeStory->get() : nullptr) {
|
||||
if (const auto photo = story->photo()) {
|
||||
return std::make_unique<HistoryView::Photo>(
|
||||
message,
|
||||
realParent,
|
||||
photo,
|
||||
spoiler);
|
||||
} else {
|
||||
return std::make_unique<HistoryView::Gif>(
|
||||
message,
|
||||
realParent,
|
||||
story->document(),
|
||||
spoiler);
|
||||
}
|
||||
}
|
||||
return std::make_unique<HistoryView::Photo>(
|
||||
message,
|
||||
realParent,
|
||||
realParent->history()->owner().photo(kLoadingStoryPhotoId),
|
||||
spoiler);
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/weak_ptr.h"
|
||||
#include "data/data_location.h"
|
||||
#include "data/data_wall_paper.h"
|
||||
|
||||
|
@ -110,6 +111,7 @@ public:
|
|||
virtual CloudImage *location() const;
|
||||
virtual PollData *poll() const;
|
||||
virtual const WallPaper *paper() const;
|
||||
virtual FullStoryId storyId() const;
|
||||
|
||||
virtual bool uploading() const;
|
||||
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const;
|
||||
|
@ -563,6 +565,30 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class MediaStory final : public Media, public base::has_weak_ptr {
|
||||
public:
|
||||
MediaStory(not_null<HistoryItem*> parent, FullStoryId storyId);
|
||||
|
||||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||
|
||||
[[nodiscard]] FullStoryId storyId() const override;
|
||||
|
||||
TextWithEntities notificationText() const override;
|
||||
QString pinnedTextSubstring() const override;
|
||||
TextForMimeData clipboardText() const override;
|
||||
|
||||
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||
std::unique_ptr<HistoryView::Media> createView(
|
||||
not_null<HistoryView::Element*> message,
|
||||
not_null<HistoryItem*> realParent,
|
||||
HistoryView::Element *replacing = nullptr) override;
|
||||
|
||||
private:
|
||||
const FullStoryId _storyId;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] TextForMimeData WithCaptionClipboardText(
|
||||
const QString &attachType,
|
||||
TextForMimeData &&caption);
|
||||
|
|
|
@ -291,7 +291,10 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
|
|||
qs(media.vemoticon()),
|
||||
media.vvalue().v);
|
||||
}, [&](const MTPDmessageMediaStory &media) -> Result {
|
||||
return nullptr; // #TODO stories
|
||||
return std::make_unique<Data::MediaStory>(item, FullStoryId{
|
||||
peerFromUser(media.vuser_id()),
|
||||
media.vid().v,
|
||||
});
|
||||
}, [](const MTPDmessageMediaEmpty &) -> Result {
|
||||
return nullptr;
|
||||
}, [](const MTPDmessageMediaUnsupported &) -> Result {
|
||||
|
@ -3594,9 +3597,29 @@ void HistoryItem::createServiceFromMtp(const MTPDmessageService &message) {
|
|||
|
||||
void HistoryItem::setMedia(const MTPMessageMedia &media) {
|
||||
_media = CreateMedia(this, media);
|
||||
checkStoryForwardInfo();
|
||||
checkBuyButton();
|
||||
}
|
||||
|
||||
void HistoryItem::checkStoryForwardInfo() {
|
||||
if (const auto storyId = _media ? _media->storyId() : FullStoryId()) {
|
||||
const auto adding = !Has<HistoryMessageForwarded>();
|
||||
if (adding) {
|
||||
AddComponents(HistoryMessageForwarded::Bit());
|
||||
}
|
||||
const auto forwarded = Get<HistoryMessageForwarded>();
|
||||
if (forwarded->story || adding) {
|
||||
const auto peer = history()->owner().peer(storyId.peer);
|
||||
forwarded->story = true;
|
||||
forwarded->originalSender = peer;
|
||||
}
|
||||
} else if (const auto forwarded = Get<HistoryMessageForwarded>()) {
|
||||
if (forwarded->story) {
|
||||
RemoveComponents(HistoryMessageForwarded::Bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryItem::applyServiceDateEdition(const MTPDmessageService &data) {
|
||||
const auto date = data.vdate().v;
|
||||
if (_date == date) {
|
||||
|
|
|
@ -192,6 +192,7 @@ public:
|
|||
[[nodiscard]] MsgId dependencyMsgId() const;
|
||||
[[nodiscard]] bool notificationReady() const;
|
||||
[[nodiscard]] PeerData *specialNotificationPeer() const;
|
||||
void checkStoryForwardInfo();
|
||||
void checkBuyButton();
|
||||
|
||||
void updateServiceText(PreparedServiceText &&text);
|
||||
|
|
|
@ -129,6 +129,7 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded
|
|||
PeerData *savedFromPeer = nullptr;
|
||||
MsgId savedFromMsgId = 0;
|
||||
bool imported = false;
|
||||
bool story = false;
|
||||
};
|
||||
|
||||
struct HistoryMessageSponsored : public RuntimeComponent<HistoryMessageSponsored, HistoryItem> {
|
||||
|
|
|
@ -248,6 +248,7 @@ TextSelection ShiftItemSelection(
|
|||
QString DateTooltipText(not_null<Element*> view) {
|
||||
const auto locale = QLocale();
|
||||
const auto format = QLocale::LongFormat;
|
||||
const auto item = view->data();
|
||||
auto dateText = locale.toString(view->dateTime(), format);
|
||||
if (const auto editedDate = view->displayedEditDate()) {
|
||||
dateText += '\n' + tr::lng_edited_date(
|
||||
|
@ -255,18 +256,22 @@ QString DateTooltipText(not_null<Element*> view) {
|
|||
lt_date,
|
||||
locale.toString(base::unixtime::parse(editedDate), format));
|
||||
}
|
||||
if (const auto forwarded = view->data()->Get<HistoryMessageForwarded>()) {
|
||||
dateText += '\n' + tr::lng_forwarded_date(
|
||||
tr::now,
|
||||
lt_date,
|
||||
locale.toString(base::unixtime::parse(forwarded->originalDate), format));
|
||||
if (forwarded->imported) {
|
||||
dateText = tr::lng_forwarded_imported(tr::now)
|
||||
+ "\n\n" + dateText;
|
||||
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
|
||||
if (!forwarded->story && forwarded->psaType.isEmpty()) {
|
||||
dateText += '\n' + tr::lng_forwarded_date(
|
||||
tr::now,
|
||||
lt_date,
|
||||
locale.toString(
|
||||
base::unixtime::parse(forwarded->originalDate),
|
||||
format));
|
||||
if (forwarded->imported) {
|
||||
dateText = tr::lng_forwarded_imported(tr::now)
|
||||
+ "\n\n" + dateText;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (view->isSignedAuthorElided()) {
|
||||
if (const auto msgsigned = view->data()->Get<HistoryMessageSigned>()) {
|
||||
if (const auto msgsigned = item->Get<HistoryMessageSigned>()) {
|
||||
dateText += '\n'
|
||||
+ tr::lng_signed_author(tr::now, lt_user, msgsigned->author);
|
||||
}
|
||||
|
|
|
@ -90,6 +90,11 @@ Gif::Gif(
|
|||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
|
||||
, _spoiler(spoiler ? std::make_unique<MediaSpoiler>() : nullptr)
|
||||
, _downloadSize(Ui::FormatSizeText(_data->size)) {
|
||||
if (const auto media = realParent->media()) {
|
||||
if (media->storyId()) {
|
||||
_story = true;
|
||||
}
|
||||
}
|
||||
setDocumentLinks(_data, realParent, [=] {
|
||||
if (!_data->createMediaView()->canBePlayed(realParent)
|
||||
|| !_data->isAnimation()
|
||||
|
@ -1441,7 +1446,9 @@ void Gif::hideSpoilers() {
|
|||
}
|
||||
|
||||
bool Gif::needsBubble() const {
|
||||
if (_data->isVideoMessage()) {
|
||||
if (_story) {
|
||||
return true;
|
||||
} else if (_data->isVideoMessage()) {
|
||||
return false;
|
||||
} else if (!_caption.isEmpty()) {
|
||||
return true;
|
||||
|
|
|
@ -219,8 +219,9 @@ private:
|
|||
mutable QImage _thumbCache;
|
||||
mutable QImage _roundingMask;
|
||||
mutable std::optional<Ui::BubbleRounding> _thumbCacheRounding;
|
||||
mutable bool _thumbCacheBlurred = false;
|
||||
mutable bool _thumbIsEllipse = false;
|
||||
mutable bool _thumbCacheBlurred : 1 = false;
|
||||
mutable bool _thumbIsEllipse : 1 = false;
|
||||
mutable bool _story : 1 = false;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -40,6 +40,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
constexpr auto kStoryWidth = 720;
|
||||
constexpr auto kStoryHeight = 1280;
|
||||
|
||||
using Data::PhotoSize;
|
||||
|
||||
} // namespace
|
||||
|
@ -67,6 +70,11 @@ Photo::Photo(
|
|||
, _data(photo)
|
||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
|
||||
, _spoiler(spoiler ? std::make_unique<MediaSpoiler>() : nullptr) {
|
||||
if (const auto media = realParent->media()) {
|
||||
if (media->storyId()) {
|
||||
_story = true;
|
||||
}
|
||||
}
|
||||
_caption = createCaption(realParent);
|
||||
create(realParent->fullId());
|
||||
}
|
||||
|
@ -167,7 +175,7 @@ QSize Photo::countOptimalSize() {
|
|||
_parent->skipBlockHeight());
|
||||
}
|
||||
|
||||
const auto dimensions = QSize(_data->width(), _data->height());
|
||||
const auto dimensions = photoSize();
|
||||
const auto scaled = CountDesiredMediaSize(dimensions);
|
||||
const auto minWidth = std::clamp(
|
||||
_parent->minWidthForMedia(),
|
||||
|
@ -210,7 +218,7 @@ QSize Photo::countCurrentSize(int newWidth) {
|
|||
? st::historyPhotoBubbleMinWidth
|
||||
: st::minPhotoSize),
|
||||
thumbMaxWidth);
|
||||
const auto dimensions = QSize(_data->width(), _data->height());
|
||||
const auto dimensions = photoSize();
|
||||
auto pix = CountPhotoMediaSize(
|
||||
CountDesiredMediaSize(dimensions),
|
||||
newWidth,
|
||||
|
@ -255,7 +263,11 @@ int Photo::adjustHeightForLessCrop(QSize dimensions, QSize current) const {
|
|||
}
|
||||
|
||||
void Photo::draw(Painter &p, const PaintContext &context) const {
|
||||
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
||||
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||
return;
|
||||
} else if (_story && _data->isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensureDataMediaCreated();
|
||||
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
|
||||
|
@ -590,11 +602,20 @@ void Photo::paintUserpicFrame(
|
|||
}
|
||||
}
|
||||
|
||||
QSize Photo::photoSize() const {
|
||||
if (_story) {
|
||||
return { kStoryWidth, kStoryHeight };
|
||||
}
|
||||
return QSize(_data->width(), _data->height());
|
||||
}
|
||||
|
||||
TextState Photo::textState(QPoint point, StateRequest request) const {
|
||||
auto result = TextState(_parent);
|
||||
|
||||
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||
return result;
|
||||
} else if (_story && _data->isNull()) {
|
||||
return result;
|
||||
}
|
||||
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
||||
auto bubble = _parent->hasBubble();
|
||||
|
@ -657,9 +678,8 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
|
|||
}
|
||||
|
||||
QSize Photo::sizeForGroupingOptimal(int maxWidth) const {
|
||||
const auto width = _data->width();
|
||||
const auto height = _data->height();
|
||||
return { std::max(width, 1), std::max(height, 1) };
|
||||
const auto size = photoSize();
|
||||
return { std::max(size.width(), 1), std::max(size.height(), 1)};
|
||||
}
|
||||
|
||||
QSize Photo::sizeForGrouping(int width) const {
|
||||
|
@ -848,8 +868,9 @@ void Photo::validateGroupedCache(
|
|||
return;
|
||||
}
|
||||
|
||||
const auto originalWidth = style::ConvertScale(_data->width());
|
||||
const auto originalHeight = style::ConvertScale(_data->height());
|
||||
const auto unscaled = photoSize();
|
||||
const auto originalWidth = style::ConvertScale(unscaled.width());
|
||||
const auto originalHeight = style::ConvertScale(unscaled.height());
|
||||
const auto pixSize = Ui::GetImageScaleSizeForGeometry(
|
||||
{ originalWidth, originalHeight },
|
||||
{ width, height });
|
||||
|
@ -1012,7 +1033,7 @@ void Photo::hideSpoilers() {
|
|||
}
|
||||
|
||||
bool Photo::needsBubble() const {
|
||||
if (!_caption.isEmpty()) {
|
||||
if (_story || !_caption.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
const auto item = _parent->data();
|
||||
|
|
|
@ -158,6 +158,8 @@ private:
|
|||
const PaintContext &context,
|
||||
QPoint photoPosition) const;
|
||||
|
||||
[[nodiscard]] QSize photoSize() const;
|
||||
|
||||
const not_null<PhotoData*> _data;
|
||||
Ui::Text::String _caption;
|
||||
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
|
||||
|
@ -165,9 +167,10 @@ private:
|
|||
const std::unique_ptr<MediaSpoiler> _spoiler;
|
||||
mutable QImage _imageCache;
|
||||
mutable std::optional<Ui::BubbleRounding> _imageCacheRounding;
|
||||
int _serviceWidth : 30 = 0;
|
||||
int _serviceWidth : 29 = 0;
|
||||
mutable int _imageCacheForum : 1 = 0;
|
||||
mutable int _imageCacheBlurred : 1 = 0;
|
||||
mutable int _story : 1 = 0;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -512,7 +512,7 @@ void SessionNavigation::showPeerByLinkResolved(
|
|||
storyId.story,
|
||||
Data::StoriesContext{ Data::StoriesContextSingle() });
|
||||
} else {
|
||||
showToast(tr::lng_confirm_phone_link_invalid(tr::now));
|
||||
showToast(tr::lng_stories_link_invalid(tr::now));
|
||||
}
|
||||
}));
|
||||
} else if (bot && resolveType == ResolveType::BotApp) {
|
||||
|
@ -2155,14 +2155,12 @@ void SessionController::openPhoto(
|
|||
not_null<PhotoData*> photo,
|
||||
FullMsgId contextId,
|
||||
MsgId topicRootId) {
|
||||
if (openStory(contextId)) {
|
||||
const auto item = session().data().message(contextId);
|
||||
if (openSharedStory(item) || openFakeItemStory(contextId)) {
|
||||
return;
|
||||
}
|
||||
_window->openInMediaView(Media::View::OpenRequest(
|
||||
this,
|
||||
photo,
|
||||
session().data().message(contextId),
|
||||
topicRootId));
|
||||
_window->openInMediaView(
|
||||
Media::View::OpenRequest(this, photo, item, topicRootId));
|
||||
}
|
||||
|
||||
void SessionController::openPhoto(
|
||||
|
@ -2176,24 +2174,34 @@ void SessionController::openDocument(
|
|||
FullMsgId contextId,
|
||||
MsgId topicRootId,
|
||||
bool showInMediaView) {
|
||||
if (openStory(contextId)) {
|
||||
const auto item = session().data().message(contextId);
|
||||
if (openSharedStory(item) || openFakeItemStory(contextId)) {
|
||||
return;
|
||||
} else if (showInMediaView) {
|
||||
_window->openInMediaView(Media::View::OpenRequest(
|
||||
this,
|
||||
document,
|
||||
session().data().message(contextId),
|
||||
topicRootId));
|
||||
_window->openInMediaView(
|
||||
Media::View::OpenRequest(this, document, item, topicRootId));
|
||||
return;
|
||||
}
|
||||
Data::ResolveDocument(
|
||||
this,
|
||||
document,
|
||||
session().data().message(contextId),
|
||||
topicRootId);
|
||||
Data::ResolveDocument(this, document, item, topicRootId);
|
||||
}
|
||||
|
||||
bool SessionController::openStory(
|
||||
bool SessionController::openSharedStory(HistoryItem *item) {
|
||||
if (const auto media = item ? item->media() : nullptr) {
|
||||
if (const auto storyId = media->storyId()) {
|
||||
const auto story = session().data().stories().lookup(storyId);
|
||||
if (story) {
|
||||
_window->openInMediaView(::Media::View::OpenRequest(
|
||||
this,
|
||||
*story,
|
||||
Data::StoriesContext{ Data::StoriesContextSingle() }));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SessionController::openFakeItemStory(
|
||||
FullMsgId fakeItemId,
|
||||
bool forceArchiveContext) {
|
||||
if (!peerIsUser(fakeItemId.peer)
|
||||
|
|
|
@ -492,7 +492,10 @@ public:
|
|||
FullMsgId contextId,
|
||||
MsgId topicRootId,
|
||||
bool showInMediaView = false);
|
||||
bool openStory(FullMsgId fakeItemId, bool forceArchiveContext = false);
|
||||
bool openSharedStory(HistoryItem *item);
|
||||
bool openFakeItemStory(
|
||||
FullMsgId fakeItemId,
|
||||
bool forceArchiveContext = false);
|
||||
|
||||
void showChooseReportMessages(
|
||||
not_null<PeerData*> peer,
|
||||
|
|
Loading…
Add table
Reference in a new issue