diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index a4ac996e8..329e16226 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -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 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index f0dddd290..23d4d4649 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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"; diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index ded87abfd..9f013e373 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -301,6 +301,7 @@ private: PossibleCoverThumbnail = 0x0400, UseTextColor = 0x0800, StoryDocument = 0x1000, + SilentVideo = 0x2000, }; using Flags = base::flags; friend constexpr bool is_flag_type(Flag) { return true; }; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 79f99591d..ac5476a1b 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -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 MediaWallPaper::createView( std::make_unique(message, _paper)); } -MediaStory::MediaStory(not_null parent, FullStoryId storyId) +MediaStory::MediaStory( + not_null 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 parent, FullStoryId storyId) } std::unique_ptr MediaStory::clone(not_null parent) { - return std::make_unique(parent, _storyId); + return std::make_unique(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 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( message, realParent, @@ -2078,19 +2103,25 @@ std::unique_ptr MediaStory::createView( } _expired = false; const auto story = *maybeStory; - realParent->setText(story->caption()); - if (const auto photo = story->photo()) { - return std::make_unique( + if (_mention) { + return std::make_unique( message, - realParent, - photo, - spoiler); + std::make_unique(message, story)); } else { - return std::make_unique( - message, - realParent, - story->document(), - spoiler); + realParent->setText(story->caption()); + if (const auto photo = story->photo()) { + return std::make_unique( + message, + realParent, + photo, + spoiler); + } else { + return std::make_unique( + message, + realParent, + story->document(), + spoiler); + } } } diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index a77e26e46..f60bc82a1 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -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 parent, FullStoryId storyId); + MediaStory( + not_null parent, + FullStoryId storyId, + bool mention); std::unique_ptr clone(not_null 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; }; diff --git a/Telegram/SourceFiles/data/data_story.cpp b/Telegram/SourceFiles/data/data_story.cpp index cf31749f4..dd11bcc0e 100644 --- a/Telegram/SourceFiles/data/data_story.cpp +++ b/Telegram/SourceFiles/data/data_story.cpp @@ -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; diff --git a/Telegram/SourceFiles/data/data_story.h b/Telegram/SourceFiles/data/data_story.h index 1f4801efd..183e46808 100644 --- a/Telegram/SourceFiles/data/data_story.h +++ b/Telegram/SourceFiles/data/data_story.h @@ -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; }; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.cpp index acce7637e..e686811aa 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.cpp @@ -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(user); + userpic = MakeUserpicThumbnail(user); _userpics.emplace(user, userpic); } result.elements.push_back({ @@ -375,19 +378,6 @@ rpl::producer ContentForSession( }; } -[[nodiscard]] std::shared_ptr PrepareThumbnail( - not_null story) { - using Result = std::shared_ptr; - const auto id = story->fullId(); - return v::match(story->media().data, [](v::null_t) -> Result { - return std::make_shared(); - }, [&](not_null photo) -> Result { - return std::make_shared(photo, id); - }, [&](not_null video) -> Result { - return std::make_shared(video, id); - }); -} - rpl::producer LastForPeer(not_null peer) { using namespace rpl::mappers; @@ -432,7 +422,7 @@ rpl::producer LastForPeer(not_null 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 LastForPeer(not_null peer) { }) | rpl::flatten_latest(); } +std::shared_ptr MakeUserpicThumbnail(not_null peer) { + return std::make_shared(peer); +} + +std::shared_ptr MakeStoryThumbnail( + not_null story) { + using Result = std::shared_ptr; + const auto id = story->fullId(); + return v::match(story->media().data, [](v::null_t) -> Result { + return std::make_shared(); + }, [&](not_null photo) -> Result { + return std::make_shared(photo, id); + }, [&](not_null video) -> Result { + return std::make_shared(video, id); + }); +} + } // namespace Dialogs::Stories diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.h b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.h index 8b25c8313..bdbb3afea 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.h @@ -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 ContentForSession( not_null session, @@ -25,4 +27,9 @@ struct Content; [[nodiscard]] rpl::producer LastForPeer(not_null peer); +[[nodiscard]] std::shared_ptr MakeUserpicThumbnail( + not_null peer); +[[nodiscard]] std::shared_ptr MakeStoryThumbnail( + not_null story); + } // namespace Dialogs::Stories diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 039a7bc8d..72745e74d 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -294,7 +294,7 @@ std::unique_ptr HistoryItem::CreateMedia( return std::make_unique(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()) { 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(); diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index a5bbb3ced..973141ba5 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -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> &users, CallId linkCallId); diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index df971938c..c89788248 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -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; }); diff --git a/Telegram/SourceFiles/history/history_item_helpers.h b/Telegram/SourceFiles/history/history_item_helpers.h index 2d9765886..e7ed74290 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.h +++ b/Telegram/SourceFiles/history/history_item_helpers.h @@ -44,6 +44,7 @@ enum class MediaCheckResult { Unsupported, Empty, HasTimeToLive, + HasStoryMention, }; [[nodiscard]] MediaCheckResult CheckMessageMedia( const MTPMessageMedia &media); diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 6a3f78b8a..f20499bb9 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -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() diff --git a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp index 252b1e621..ec292be93 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp @@ -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() { diff --git a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.h b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.h index 53431ad2b..edb79ad52 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.h +++ b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.h @@ -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, diff --git a/Telegram/SourceFiles/history/view/media/history_view_service_box.cpp b/Telegram/SourceFiles/history/view/media/history_view_service_box.cpp index e129d28f5..e425bdf65 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_service_box.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_service_box.cpp @@ -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(), diff --git a/Telegram/SourceFiles/history/view/media/history_view_service_box.h b/Telegram/SourceFiles/history/view/media/history_view_service_box.h index a986b01a8..94347d401 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_service_box.h +++ b/Telegram/SourceFiles/history/view/media/history_view_service_box.h @@ -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, diff --git a/Telegram/SourceFiles/history/view/media/history_view_story_mention.cpp b/Telegram/SourceFiles/history/view/media/history_view_story_mention.cpp new file mode 100644 index 000000000..fb9f62f29 --- /dev/null +++ b/Telegram/SourceFiles/history/view/media/history_view_story_mention.cpp @@ -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 parent, + not_null 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(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 StoryMention::stickerTakePlayer( + not_null data, + const Lottie::ColorReplacements *replacements) { + return nullptr; +} + +bool StoryMention::hasHeavyPart() { + return _subscribed; +} + +void StoryMention::unloadHeavyPart() { + if (_subscribed) { + _subscribed = false; + _thumbnail->subscribeToUpdates(nullptr); + } +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_story_mention.h b/Telegram/SourceFiles/history/view/media/history_view_story_mention.h new file mode 100644 index 000000000..9cdacebcb --- /dev/null +++ b/Telegram/SourceFiles/history/view/media/history_view_story_mention.h @@ -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 parent, not_null 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 stickerTakePlayer( + not_null data, + const Lottie::ColorReplacements *replacements) override; + + bool hasHeavyPart() override; + void unloadHeavyPart() override; + +private: + using Thumbnail = Dialogs::Stories::Thumbnail; + + const not_null _parent; + const not_null _story; + std::shared_ptr _thumbnail; + bool _thumbnailFromStory = false; + bool _subscribed = false; + +}; + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp index 0cd31f483..ad5dfcac1 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp @@ -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() { diff --git a/Telegram/SourceFiles/history/view/media/history_view_theme_document.h b/Telegram/SourceFiles/history/view/media/history_view_theme_document.h index f923ca253..94549e951 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_theme_document.h +++ b/Telegram/SourceFiles/history/view/media/history_view_theme_document.h @@ -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, diff --git a/Telegram/SourceFiles/history/view/media/history_view_userpic_suggestion.cpp b/Telegram/SourceFiles/history/view/media/history_view_userpic_suggestion.cpp index 7056a7780..5c37ccb04 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_userpic_suggestion.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_userpic_suggestion.cpp @@ -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() { diff --git a/Telegram/SourceFiles/history/view/media/history_view_userpic_suggestion.h b/Telegram/SourceFiles/history/view/media/history_view_userpic_suggestion.h index bed87c324..42ad65f44 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_userpic_suggestion.h +++ b/Telegram/SourceFiles/history/view/media/history_view_userpic_suggestion.h @@ -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, diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl index b9197b033..20a0dd4f4 100644 --- a/Telegram/SourceFiles/mtproto/scheme/api.tl +++ b/Telegram/SourceFiles/mtproto/scheme/api.tl @@ -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 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 media:MessageMedia privacy:flags.2?Vector 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 media:MessageMedia privacy:flags.2?Vector views:flags.3?StoryViews = StoryItem; userStories#8611a200 flags:# user_id:long max_read_id:flags.0?int stories:Vector = UserStories; @@ -2087,7 +2087,7 @@ chatlists.hideChatlistUpdates#66e486fb chatlist:InputChatlist = Bool; chatlists.getLeaveChatlistSuggestions#fdbcd714 chatlist:InputChatlist = Vector; chatlists.leaveChatlist#74fae13a chatlist:InputChatlist peers:Vector = Updates; -stories.sendStory#424cd47a flags:# pinned:flags.2?true media:InputMedia caption:flags.0?string entities:flags.1?Vector privacy_rules:Vector 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 privacy_rules:Vector 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 privacy_rules:flags.2?Vector = Updates; stories.deleteStories#b5d501d7 id:Vector = Vector; stories.togglePinned#51602944 id:Vector pinned:Bool = Vector; diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 855f8f7b7..ed00cd280 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 855f8f7b75f83127642eb5bbb1457f2087b3f1de +Subproject commit ed00cd28098e1b04979e7b2dd3a582b7a8a6713a