Implement story mention messages.

This commit is contained in:
John Preston 2023-06-29 14:48:14 +04:00
parent 75d4ba7be1
commit 3d795f2f67
26 changed files with 386 additions and 84 deletions

View File

@ -700,6 +700,8 @@ PRIVATE
history/view/media/history_view_sticker_player.cpp
history/view/media/history_view_sticker_player.h
history/view/media/history_view_sticker_player_abstract.h
history/view/media/history_view_story_mention.cpp
history/view/media/history_view_story_mention.h
history/view/media/history_view_theme_document.cpp
history/view/media/history_view_theme_document.h
history/view/media/history_view_userpic_suggestion.cpp

View File

@ -1574,6 +1574,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_topic_icon_changed" = "{from} changed the {link} icon to {emoji}";
"lng_action_topic_icon_removed" = "{from} removed the {link} icon";
"lng_action_shared_chat_with_bot" = "You shared {chat} with {bot}";
"lng_action_story_mention_me" = "You mentioned {user} in a story";
"lng_action_story_mention" = "{user} mentioned you in a story";
"lng_action_story_mention_button" = "View Story";
"lng_action_story_mention_me_unavailable" = "The story where you mentioned {user} is no longer available.";
"lng_action_story_mention_unavailable" = "The story where {user} mentioned you is no longer available.";
"lng_premium_gift_duration_months#one" = "for {count} month";
"lng_premium_gift_duration_months#other" = "for {count} months";

View File

@ -301,6 +301,7 @@ private:
PossibleCoverThumbnail = 0x0400,
UseTextColor = 0x0800,
StoryDocument = 0x1000,
SilentVideo = 0x2000,
};
using Flags = base::flags<Flag>;
friend constexpr bool is_flag_type(Flag) { return true; };

View File

@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_slot_machine.h"
#include "history/view/media/history_view_dice.h"
#include "history/view/media/history_view_service_box.h"
#include "history/view/media/history_view_story_mention.h"
#include "history/view/media/history_view_premium_gift.h"
#include "history/view/media/history_view_userpic_suggestion.h"
#include "dialogs/ui/dialogs_message_view.h"
@ -415,6 +416,10 @@ bool Media::storyExpired() const {
return false;
}
bool Media::storyMention() const {
return false;
}
bool Media::uploading() const {
return false;
}
@ -1979,20 +1984,31 @@ std::unique_ptr<HistoryView::Media> MediaWallPaper::createView(
std::make_unique<HistoryView::ThemeDocumentBox>(message, _paper));
}
MediaStory::MediaStory(not_null<HistoryItem*> parent, FullStoryId storyId)
MediaStory::MediaStory(
not_null<HistoryItem*> parent,
FullStoryId storyId,
bool mention)
: Media(parent)
, _storyId(storyId) {
, _storyId(storyId)
, _mention(mention) {
const auto stories = &parent->history()->owner().stories();
if (const auto maybeStory = stories->lookup(storyId)) {
parent->setText((*maybeStory)->caption());
if (!_mention) {
parent->setText((*maybeStory)->caption());
}
} else {
if (maybeStory.error() == NoStory::Unknown) {
stories->resolve(storyId, crl::guard(this, [=] {
if (const auto maybeStory = stories->lookup(storyId)) {
parent->setText((*maybeStory)->caption());
if (!_mention) {
parent->setText((*maybeStory)->caption());
}
} else {
_expired = true;
}
if (_mention) {
parent->updateStoryMentionText();
}
parent->history()->owner().requestItemViewRefresh(parent);
}));
} else {
@ -2002,7 +2018,7 @@ MediaStory::MediaStory(not_null<HistoryItem*> parent, FullStoryId storyId)
}
std::unique_ptr<Media> MediaStory::clone(not_null<HistoryItem*> parent) {
return std::make_unique<MediaStory>(parent, _storyId);
return std::make_unique<MediaStory>(parent, _storyId, false);
}
FullStoryId MediaStory::storyId() const {
@ -2013,6 +2029,10 @@ bool MediaStory::storyExpired() const {
return _expired;
}
bool MediaStory::storyMention() const {
return _mention;
}
TextWithEntities MediaStory::notificationText() const {
const auto stories = &parent()->history()->owner().stories();
const auto maybeStory = stories->lookup(_storyId);
@ -2064,12 +2084,17 @@ std::unique_ptr<HistoryView::Media> MediaStory::createView(
const auto stories = &parent()->history()->owner().stories();
const auto maybeStory = stories->lookup(_storyId);
if (!maybeStory) {
realParent->setText(TextWithEntities());
if (!_mention) {
realParent->setText(TextWithEntities());
}
if (maybeStory.error() == Data::NoStory::Deleted) {
_expired = true;
return nullptr;
}
_expired = false;
if (_mention) {
return nullptr;
}
return std::make_unique<HistoryView::Photo>(
message,
realParent,
@ -2078,19 +2103,25 @@ std::unique_ptr<HistoryView::Media> MediaStory::createView(
}
_expired = false;
const auto story = *maybeStory;
realParent->setText(story->caption());
if (const auto photo = story->photo()) {
return std::make_unique<HistoryView::Photo>(
if (_mention) {
return std::make_unique<HistoryView::ServiceBox>(
message,
realParent,
photo,
spoiler);
std::make_unique<HistoryView::StoryMention>(message, story));
} else {
return std::make_unique<HistoryView::Gif>(
message,
realParent,
story->document(),
spoiler);
realParent->setText(story->caption());
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);
}
}
}

View File

@ -115,6 +115,7 @@ public:
virtual const WallPaper *paper() const;
virtual FullStoryId storyId() const;
virtual bool storyExpired() const;
virtual bool storyMention() const;
virtual bool uploading() const;
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const;
@ -570,12 +571,16 @@ private:
class MediaStory final : public Media, public base::has_weak_ptr {
public:
MediaStory(not_null<HistoryItem*> parent, FullStoryId storyId);
MediaStory(
not_null<HistoryItem*> parent,
FullStoryId storyId,
bool mention);
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
FullStoryId storyId() const override;
bool storyExpired() const override;
bool storyMention() const override;
TextWithEntities notificationText() const override;
QString pinnedTextSubstring() const override;
@ -594,6 +599,7 @@ public:
private:
const FullStoryId _storyId;
const bool _mention = false;
bool _expired = false;
};

View File

@ -248,28 +248,24 @@ bool Story::pinned() const {
return _pinned;
}
void Story::setIsPublic(bool isPublic) {
_isPublic = isPublic;
}
bool Story::isPublic() const {
return _isPublic;
}
void Story::setCloseFriends(bool closeFriends) {
_closeFriends = closeFriends;
}
bool Story::closeFriends() const {
return _closeFriends;
}
bool Story::forbidsForward() const {
return _noForwards;
}
bool Story::canDownload() const {
return _peer->isSelf();
return !forbidsForward() || _peer->isSelf();
}
bool Story::canShare() const {
return isPublic() && (pinned() || !expired());
return isPublic() && !forbidsForward() && (pinned() || !expired());
}
bool Story::canDelete() const {
@ -375,6 +371,7 @@ bool Story::applyChanges(StoryMedia media, const MTPDstoryItem &data) {
const auto pinned = data.is_pinned();
const auto isPublic = data.is_public();
const auto closeFriends = data.is_close_friends();
const auto noForwards = data.is_noforwards();
auto caption = TextWithEntities{
data.vcaption().value_or_empty(),
Api::EntitiesFromMTP(
@ -400,6 +397,7 @@ bool Story::applyChanges(StoryMedia media, const MTPDstoryItem &data) {
|| (_pinned != pinned)
|| (_isPublic != isPublic)
|| (_closeFriends != closeFriends)
|| (_noForwards != noForwards)
|| (_caption != caption)
|| (views >= 0 && _views != views)
|| (_recentViewers != recent);
@ -410,6 +408,7 @@ bool Story::applyChanges(StoryMedia media, const MTPDstoryItem &data) {
_pinned = pinned;
_isPublic = isPublic;
_closeFriends = closeFriends;
_noForwards = noForwards;
_caption = std::move(caption);
if (views >= 0) {
_views = views;

View File

@ -86,10 +86,9 @@ public:
void setPinned(bool pinned);
[[nodiscard]] bool pinned() const;
void setIsPublic(bool isPublic);
[[nodiscard]] bool isPublic() const;
void setCloseFriends(bool closeFriends);
[[nodiscard]] bool closeFriends() const;
[[nodiscard]] bool forbidsForward() const;
[[nodiscard]] bool canDownload() const;
[[nodiscard]] bool canShare() const;
@ -128,6 +127,7 @@ private:
bool _pinned : 1 = false;
bool _isPublic : 1 = false;
bool _closeFriends : 1 = false;
bool _noForwards : 1 = false;
};

View File

@ -144,11 +144,12 @@ QImage PeerUserpic::image(int size) {
const auto good = (_frame.width() == size * _frame.devicePixelRatio());
const auto key = _peer->userpicUniqueKey(_subscribed->view);
if (!good || (_subscribed->key != key && !waitingUserpicLoad())) {
const auto ratio = style::DevicePixelRatio();
_subscribed->key = key;
_frame = QImage(
QSize(size, size) * style::DevicePixelRatio(),
QSize(size, size) * ratio,
QImage::Format_ARGB32_Premultiplied);
_frame.setDevicePixelRatio(style::DevicePixelRatio());
_frame.setDevicePixelRatio(ratio);
_frame.fill(Qt::transparent);
auto p = Painter(&_frame);
@ -216,6 +217,7 @@ QImage StoryThumbnail::image(int size) {
Qt::SmoothTransformation);
}
_prepared = Images::Circle(std::move(_prepared));
_prepared.setDevicePixelRatio(ratio);
}
return _prepared;
}
@ -309,6 +311,7 @@ QImage EmptyThumbnail::image(int size) {
QSize(size, size) * ratio,
QImage::Format_ARGB32_Premultiplied);
_cached.fill(Qt::black);
_cached.setDevicePixelRatio(ratio);
}
return _cached;
}
@ -336,7 +339,7 @@ Content State::next() {
if (const auto i = _userpics.find(user); i != end(_userpics)) {
userpic = i->second;
} else {
userpic = std::make_shared<PeerUserpic>(user);
userpic = MakeUserpicThumbnail(user);
_userpics.emplace(user, userpic);
}
result.elements.push_back({
@ -375,19 +378,6 @@ rpl::producer<Content> ContentForSession(
};
}
[[nodiscard]] std::shared_ptr<Thumbnail> PrepareThumbnail(
not_null<Data::Story*> story) {
using Result = std::shared_ptr<Thumbnail>;
const auto id = story->fullId();
return v::match(story->media().data, [](v::null_t) -> Result {
return std::make_shared<EmptyThumbnail>();
}, [&](not_null<PhotoData*> photo) -> Result {
return std::make_shared<PhotoThumbnail>(photo, id);
}, [&](not_null<DocumentData*> video) -> Result {
return std::make_shared<VideoThumbnail>(video, id);
});
}
rpl::producer<Content> LastForPeer(not_null<PeerData*> peer) {
using namespace rpl::mappers;
@ -432,7 +422,7 @@ rpl::producer<Content> LastForPeer(not_null<PeerData*> peer) {
result.elements.reserve(ids.size());
result.elements.push_back({
.id = uint64(id),
.thumbnail = PrepareThumbnail(*maybe),
.thumbnail = MakeStoryThumbnail(*maybe),
.unread = (id > readTill),
});
}
@ -459,4 +449,21 @@ rpl::producer<Content> LastForPeer(not_null<PeerData*> peer) {
}) | rpl::flatten_latest();
}
std::shared_ptr<Thumbnail> MakeUserpicThumbnail(not_null<PeerData*> peer) {
return std::make_shared<PeerUserpic>(peer);
}
std::shared_ptr<Thumbnail> MakeStoryThumbnail(
not_null<Data::Story*> story) {
using Result = std::shared_ptr<Thumbnail>;
const auto id = story->fullId();
return v::match(story->media().data, [](v::null_t) -> Result {
return std::make_shared<EmptyThumbnail>();
}, [&](not_null<PhotoData*> photo) -> Result {
return std::make_shared<PhotoThumbnail>(photo, id);
}, [&](not_null<DocumentData*> video) -> Result {
return std::make_shared<VideoThumbnail>(video, id);
});
}
} // namespace Dialogs::Stories

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data {
enum class StorySourcesList : uchar;
class Story;
} // namespace Data
namespace Main {
@ -18,6 +19,7 @@ class Session;
namespace Dialogs::Stories {
struct Content;
class Thumbnail;
[[nodiscard]] rpl::producer<Content> ContentForSession(
not_null<Main::Session*> session,
@ -25,4 +27,9 @@ struct Content;
[[nodiscard]] rpl::producer<Content> LastForPeer(not_null<PeerData*> peer);
[[nodiscard]] std::shared_ptr<Thumbnail> MakeUserpicThumbnail(
not_null<PeerData*> peer);
[[nodiscard]] std::shared_ptr<Thumbnail> MakeStoryThumbnail(
not_null<Data::Story*> story);
} // namespace Dialogs::Stories

View File

@ -294,7 +294,7 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
return std::make_unique<Data::MediaStory>(item, FullStoryId{
peerFromUser(media.vuser_id()),
media.vid().v,
});
}, media.is_via_mention());
}, [](const MTPDmessageMediaEmpty &) -> Result {
return nullptr;
}, [](const MTPDmessageMediaUnsupported &) -> Result {
@ -330,6 +330,10 @@ HistoryItem::HistoryItem(
} else if (checked == MediaCheckResult::HasTimeToLive) {
createServiceFromMtp(data);
applyTTL(data);
} else if (checked == MediaCheckResult::HasStoryMention) {
setMedia(*data.vmedia());
createServiceFromMtp(data);
applyTTL(data);
} else {
createComponents(data);
if (const auto media = data.vmedia()) {
@ -1055,6 +1059,10 @@ void HistoryItem::updateServiceText(PreparedServiceText &&text) {
_history->owner().updateDependentMessages(this);
}
void HistoryItem::updateStoryMentionText() {
setServiceText(prepareStoryMentionText());
}
HistoryMessageReplyMarkup *HistoryItem::inlineReplyMarkup() {
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
if (markup->data.flags & ReplyMarkupFlag::Inline) {
@ -3386,15 +3394,13 @@ void HistoryItem::refreshSentMedia(const MTPMessageMedia *media) {
void HistoryItem::createServiceFromMtp(const MTPDmessage &message) {
AddComponents(HistoryServiceData::Bit());
const auto unread = message.is_media_unread();
const auto media = message.vmedia();
Assert(media != nullptr);
const auto mediaType = media->type();
switch (mediaType) {
case mtpc_messageMediaPhoto: {
if (message.is_media_unread()) {
const auto &photo = media->c_messageMediaPhoto();
const auto ttl = photo.vttl_seconds();
media->match([&](const MTPDmessageMediaPhoto &data) {
if (unread) {
const auto ttl = data.vttl_seconds();
Assert(ttl != nullptr);
setSelfDestruct(HistoryServiceSelfDestruct::Type::Photo, ttl->v);
@ -3417,11 +3423,9 @@ void HistoryItem::createServiceFromMtp(const MTPDmessage &message) {
tr::lng_ttl_photo_expired(tr::now, Ui::Text::WithEntities)
});
}
} break;
case mtpc_messageMediaDocument: {
if (message.is_media_unread()) {
const auto &document = media->c_messageMediaDocument();
const auto ttl = document.vttl_seconds();
}, [&](const MTPDmessageMediaDocument &data) {
if (unread) {
const auto ttl = data.vttl_seconds();
Assert(ttl != nullptr);
setSelfDestruct(HistoryServiceSelfDestruct::Type::Video, ttl->v);
@ -3444,10 +3448,11 @@ void HistoryItem::createServiceFromMtp(const MTPDmessage &message) {
tr::lng_ttl_video_expired(tr::now, Ui::Text::WithEntities)
});
}
} break;
default: Unexpected("Media type in HistoryItem::createServiceFromMtp()");
}
}, [&](const MTPDmessageMediaStory &data) {
setServiceText(prepareStoryMentionText());
}, [](const auto &) {
Unexpected("Media type in HistoryItem::createServiceFromMtp()");
});
if (const auto reactions = message.vreactions()) {
updateReactions(reactions);
@ -4816,6 +4821,28 @@ PreparedServiceText HistoryItem::preparePaymentSentText() {
return result;
}
PreparedServiceText HistoryItem::prepareStoryMentionText() {
auto result = PreparedServiceText();
const auto peer = history()->peer;
result.links.push_back(peer->createOpenLink());
const auto phrase = (this->media() && this->media()->storyExpired())
? (out()
? tr::lng_action_story_mention_me_unavailable
: tr::lng_action_story_mention_unavailable)
: (out()
? tr::lng_action_story_mention_me
: tr::lng_action_story_mention);
result.text = phrase(
tr::now,
lt_user,
Ui::Text::Wrapped(
Ui::Text::Bold(peer->shortName()),
EntityType::CustomUrl,
u"internal:index"_q + QChar(1)),
Ui::Text::WithEntities);
return result;
}
PreparedServiceText HistoryItem::prepareCallScheduledText(
TimeId scheduleDate) {
const auto call = Get<HistoryServiceOngoingCall>();

View File

@ -196,6 +196,7 @@ public:
void checkBuyButton();
void updateServiceText(PreparedServiceText &&text);
void updateStoryMentionText();
[[nodiscard]] UserData *viaBot() const;
[[nodiscard]] UserData *getMessageBot() const;
@ -608,6 +609,7 @@ private:
[[nodiscard]] PreparedServiceText preparePinnedText();
[[nodiscard]] PreparedServiceText prepareGameScoreText();
[[nodiscard]] PreparedServiceText preparePaymentSentText();
[[nodiscard]] PreparedServiceText prepareStoryMentionText();
[[nodiscard]] PreparedServiceText prepareInvitedToCallText(
const std::vector<not_null<UserData*>> &users,
CallId linkCallId);

View File

@ -450,7 +450,9 @@ MediaCheckResult CheckMessageMedia(const MTPMessageMedia &media) {
}, [](const MTPDmessageMediaDice &) {
return Result::Good;
}, [](const MTPDmessageMediaStory &data) {
return Result::Good;
return data.is_via_mention()
? Result::HasStoryMention
: Result::Good;
}, [](const MTPDmessageMediaUnsupported &) {
return Result::Unsupported;
});

View File

@ -44,6 +44,7 @@ enum class MediaCheckResult {
Unsupported,
Empty,
HasTimeToLive,
HasStoryMention,
};
[[nodiscard]] MediaCheckResult CheckMessageMedia(
const MTPMessageMedia &media);

View File

@ -841,13 +841,18 @@ void Element::validateText() {
const auto item = data();
const auto &text = item->_text;
const auto media = item->media();
const auto storyMention = media && media->storyMention();
if (media && media->storyExpired()) {
_media = nullptr;
if (_text.isEmpty()) {
setTextWithLinks(
Ui::Text::Italic(u"This story has expired"_q));
if (!storyMention) {
if (_text.isEmpty()) {
setTextWithLinks(
Ui::Text::Italic(u"This story has expired"_q));
}
return;
}
} else if (_text.isEmpty() == text.empty()) {
}
if (_text.isEmpty() == text.empty()) {
} else if (_flags & Flag::ServiceMessage) {
const auto contextDependentText = contextDependentServiceText();
const auto &markedText = contextDependentText.text.empty()

View File

@ -54,8 +54,8 @@ QString PremiumGift::title() {
return tr::lng_premium_summary_title(tr::now);
}
QString PremiumGift::subtitle() {
return FormatGiftMonths(_gift->months());
TextWithEntities PremiumGift::subtitle() {
return { FormatGiftMonths(_gift->months()) };
}
QString PremiumGift::button() {

View File

@ -26,7 +26,7 @@ public:
int top() override;
QSize size() override;
QString title() override;
QString subtitle() override;
TextWithEntities subtitle() override;
QString button() override;
void draw(
Painter &p,

View File

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "ui/chat/chat_style.h"
#include "ui/effects/ripple_animation.h"
#include "ui/text/text_utilities.h"
#include "ui/painter.h"
#include "styles/style_chat.h"
#include "styles/style_premium.h"
@ -57,8 +58,15 @@ ServiceBox::ServiceBox(
_maxWidth)
, _subtitle(
st::premiumPreviewAbout.style,
_content->subtitle(),
kDefaultTextOptions,
Ui::Text::Filtered(
_content->subtitle(),
{
EntityType::Bold,
EntityType::StrikeOut,
EntityType::Underline,
EntityType::Italic,
}),
kMarkupTextOptions,
_maxWidth)
, _size(
st::msgServiceGiftBoxSize.width(),

View File

@ -22,7 +22,7 @@ public:
[[nodiscard]] virtual int top() = 0;
[[nodiscard]] virtual QSize size() = 0;
[[nodiscard]] virtual QString title() = 0;
[[nodiscard]] virtual QString subtitle() = 0;
[[nodiscard]] virtual TextWithEntities subtitle() = 0;
[[nodiscard]] virtual QString button() = 0;
virtual void draw(
Painter &p,

View File

@ -0,0 +1,134 @@
/*
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 "history/view/media/history_view_story_mention.h"
#include "core/click_handler_types.h" // ClickHandlerContext
#include "data/data_document.h"
#include "data/data_photo.h"
#include "data/data_user.h"
#include "data/data_photo_media.h"
#include "data/data_file_click_handler.h"
#include "data/data_session.h"
#include "data/data_stories.h"
#include "dialogs/ui/dialogs_stories_content.h"
#include "dialogs/ui/dialogs_stories_list.h"
#include "editor/photo_editor_common.h"
#include "editor/photo_editor_layer_widget.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/view/media/history_view_sticker_player_abstract.h"
#include "history/view/history_view_element.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "window/window_session_controller.h"
#include "ui/boxes/confirm_box.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/painter.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "api/api_peer_photo.h"
#include "settings/settings_information.h" // UpdatePhotoLocally
#include "styles/style_chat.h"
namespace HistoryView {
namespace {
} // namespace
StoryMention::StoryMention(
not_null<Element*> parent,
not_null<Data::Story*> story)
: _parent(parent)
, _story(story) {
}
StoryMention::~StoryMention() = default;
int StoryMention::top() {
return st::msgServiceGiftBoxButtonMargins.top();
}
QSize StoryMention::size() {
return { st::msgServicePhotoWidth, st::msgServicePhotoWidth };
}
QString StoryMention::title() {
return QString();
}
QString StoryMention::button() {
return tr::lng_action_story_mention_button(tr::now);
}
TextWithEntities StoryMention::subtitle() {
return _parent->data()->notificationText();
}
ClickHandlerPtr StoryMention::createViewLink() {
const auto itemId = _parent->data()->fullId();
return std::make_shared<LambdaClickHandler>(crl::guard(this, [=](
ClickContext) {
if (const auto photo = _story->photo()) {
_parent->delegate()->elementOpenPhoto(photo, itemId);
} else if (const auto video = _story->document()) {
_parent->delegate()->elementOpenDocument(video, itemId);
}
}));
}
void StoryMention::draw(
Painter &p,
const PaintContext &context,
const QRect &geometry) {
const auto showStory = !_story->forbidsForward();
if (!_thumbnail || _thumbnailFromStory != showStory) {
using namespace Dialogs::Stories;
const auto item = _parent->data();
const auto history = item->history();
_thumbnail = showStory
? MakeStoryThumbnail(_story)
: MakeUserpicThumbnail(item->out()
? history->session().user()
: history->peer);
_thumbnailFromStory = showStory;
_subscribed = false;
}
if (!_subscribed) {
_thumbnail->subscribeToUpdates([=] {
_parent->data()->history()->owner().requestViewRepaint(_parent);
});
_subscribed = true;
}
p.drawImage(
geometry.topLeft(),
_thumbnail->image(st::msgServicePhotoWidth));
}
void StoryMention::stickerClearLoopPlayed() {
}
std::unique_ptr<StickerPlayer> StoryMention::stickerTakePlayer(
not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) {
return nullptr;
}
bool StoryMention::hasHeavyPart() {
return _subscribed;
}
void StoryMention::unloadHeavyPart() {
if (_subscribed) {
_subscribed = false;
_thumbnail->subscribeToUpdates(nullptr);
}
}
} // namespace HistoryView

View File

@ -0,0 +1,65 @@
/*
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 "history/view/media/history_view_media.h"
#include "history/view/media/history_view_media_unwrapped.h"
#include "history/view/media/history_view_service_box.h"
namespace Data {
class Story;
} // namespace Data
namespace Dialogs::Stories {
class Thumbnail;
} // namespace Dialogs::Stories
namespace HistoryView {
class StoryMention final
: public ServiceBoxContent
, public base::has_weak_ptr {
public:
StoryMention(not_null<Element*> parent, not_null<Data::Story*> story);
~StoryMention();
int top() override;
QSize size() override;
QString title() override;
TextWithEntities subtitle() override;
QString button() override;
void draw(
Painter &p,
const PaintContext &context,
const QRect &geometry) override;
ClickHandlerPtr createViewLink() override;
bool hideServiceText() override {
return true;
}
void stickerClearLoopPlayed() override;
std::unique_ptr<StickerPlayer> stickerTakePlayer(
not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) override;
bool hasHeavyPart() override;
void unloadHeavyPart() override;
private:
using Thumbnail = Dialogs::Stories::Thumbnail;
const not_null<Element*> _parent;
const not_null<Data::Story*> _story;
std::shared_ptr<Thumbnail> _thumbnail;
bool _thumbnailFromStory = false;
bool _subscribed = false;
};
} // namespace HistoryView

View File

@ -460,8 +460,8 @@ QString ThemeDocumentBox::title() {
return QString();
}
QString ThemeDocumentBox::subtitle() {
return _parent->data()->notificationText().text;
TextWithEntities ThemeDocumentBox::subtitle() {
return _parent->data()->notificationText();
}
QString ThemeDocumentBox::button() {

View File

@ -99,7 +99,7 @@ public:
int top() override;
QSize size() override;
QString title() override;
QString subtitle() override;
TextWithEntities subtitle() override;
QString button() override;
void draw(
Painter &p,

View File

@ -211,8 +211,8 @@ QString UserpicSuggestion::button() {
: tr::lng_action_suggested_photo_button(tr::now);
}
QString UserpicSuggestion::subtitle() {
return _photo.parent()->data()->notificationText().text;
TextWithEntities UserpicSuggestion::subtitle() {
return _photo.parent()->data()->notificationText();
}
ClickHandlerPtr UserpicSuggestion::createViewLink() {

View File

@ -30,7 +30,7 @@ public:
int top() override;
QSize size() override;
QString title() override;
QString subtitle() override;
TextWithEntities subtitle() override;
QString button() override;
void draw(
Painter &p,

View File

@ -1135,7 +1135,7 @@ codeSettings#ad253d78 flags:# allow_flashcall:flags.0?true current_number:flags.
wallPaperSettings#1dc1bca4 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int second_background_color:flags.4?int third_background_color:flags.5?int fourth_background_color:flags.6?int intensity:flags.3?int rotation:flags.4?int = WallPaperSettings;
autoDownloadSettings#baa57628 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true photo_size_max:int video_size_max:long file_size_max:long video_upload_maxbitrate:int small_queue_active_operations_max:int large_queue_active_operations_max:int = AutoDownloadSettings;
autoDownloadSettings#baa57628 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true stories_preload:flags.4?true photo_size_max:int video_size_max:long file_size_max:long video_upload_maxbitrate:int small_queue_active_operations_max:int large_queue_active_operations_max:int = AutoDownloadSettings;
account.autoDownloadSettings#63cacf26 low:AutoDownloadSettings medium:AutoDownloadSettings high:AutoDownloadSettings = account.AutoDownloadSettings;
@ -1530,7 +1530,7 @@ storyViews#d36760cf flags:# views_count:int recent_viewers:flags.0?Vector<long>
storyItemDeleted#51e6ee4f id:int = StoryItem;
storyItemSkipped#693206a2 id:int date:int expire_date:int = StoryItem;
storyItem#562aa637 flags:# pinned:flags.5?true public:flags.7?true close_friends:flags.8?true min:flags.9?true id:int date:int expire_date:int caption:flags.0?string entities:flags.1?Vector<MessageEntity> media:MessageMedia privacy:flags.2?Vector<PrivacyRule> views:flags.3?StoryViews = StoryItem;
storyItem#562aa637 flags:# pinned:flags.5?true public:flags.7?true close_friends:flags.8?true min:flags.9?true noforwards:flags.10?true edited:flags.11?true id:int date:int expire_date:int caption:flags.0?string entities:flags.1?Vector<MessageEntity> media:MessageMedia privacy:flags.2?Vector<PrivacyRule> views:flags.3?StoryViews = StoryItem;
userStories#8611a200 flags:# user_id:long max_read_id:flags.0?int stories:Vector<StoryItem> = UserStories;
@ -2087,7 +2087,7 @@ chatlists.hideChatlistUpdates#66e486fb chatlist:InputChatlist = Bool;
chatlists.getLeaveChatlistSuggestions#fdbcd714 chatlist:InputChatlist = Vector<Peer>;
chatlists.leaveChatlist#74fae13a chatlist:InputChatlist peers:Vector<InputPeer> = Updates;
stories.sendStory#424cd47a flags:# pinned:flags.2?true media:InputMedia caption:flags.0?string entities:flags.1?Vector<MessageEntity> privacy_rules:Vector<InputPrivacyRule> random_id:long period:flags.3?int = Updates;
stories.sendStory#424cd47a flags:# pinned:flags.2?true noforwards:flags.4?true media:InputMedia caption:flags.0?string entities:flags.1?Vector<MessageEntity> privacy_rules:Vector<InputPrivacyRule> random_id:long period:flags.3?int = Updates;
stories.editStory#2aae7a41 flags:# id:int media:flags.0?InputMedia caption:flags.1?string entities:flags.1?Vector<MessageEntity> privacy_rules:flags.2?Vector<InputPrivacyRule> = Updates;
stories.deleteStories#b5d501d7 id:Vector<int> = Vector<int>;
stories.togglePinned#51602944 id:Vector<int> pinned:Bool = Vector<int>;

@ -1 +1 @@
Subproject commit 855f8f7b75f83127642eb5bbb1457f2087b3f1de
Subproject commit ed00cd28098e1b04979e7b2dd3a582b7a8a6713a