mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Implement story mention messages.
This commit is contained in:
parent
75d4ba7be1
commit
3d795f2f67
26 changed files with 386 additions and 84 deletions
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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; };
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -44,6 +44,7 @@ enum class MediaCheckResult {
|
|||
Unsupported,
|
||||
Empty,
|
||||
HasTimeToLive,
|
||||
HasStoryMention,
|
||||
};
|
||||
[[nodiscard]] MediaCheckResult CheckMessageMedia(
|
||||
const MTPMessageMedia &media);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
Loading…
Add table
Reference in a new issue