Add spoilers to shared GIFs.

This commit is contained in:
John Preston
2025-07-22 12:06:00 +04:00
parent 62aec34507
commit 4b85c9e2df
3 changed files with 164 additions and 19 deletions
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/media/info_media_list_section.h"
#include "info/info_controller.h"
#include "layout/layout_selection.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "lang/lang_keys.h"
#include "history/history.h"
@@ -59,6 +60,16 @@ Provider::Provider(not_null<AbstractController*> controller)
layout.second.item->invalidateCache();
}
}, _lifetime);
_controller->session().appConfig().ignoredRestrictionReasonsChanges(
) | rpl::start_with_next([=](std::vector<QString> &&changed) {
const auto sensitive = Data::UnavailableReason::Sensitive();
if (ranges::contains(changed, sensitive.reason)) {
for (auto &[id, layout] : _layouts) {
layout.item->maybeClearSensitiveSpoiler();
}
}
}, _lifetime);
}
Type Provider::type() {
+133 -15
View File
@@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_components.h"
#include "history/history_item_helpers.h"
#include "history/view/history_view_cursor_state.h"
#include "history/view/media/history_view_media_common.h"
#include "history/view/media/history_view_document.h" // DrawThumbnailAsSongCover
#include "base/unixtime.h"
#include "ui/effects/round_checkbox.h"
@@ -98,6 +99,27 @@ constexpr auto kStoryRatio = 1.46;
}
}
void PaintSensitiveTag(Painter &p, QRect r) {
auto text = Ui::Text::String();
text.setText(
st::semiboldTextStyle,
tr::lng_sensitive_tag(tr::now));
const auto width = text.maxWidth();
const auto inner = QRect(0, 0, width, text.minHeight());
const auto outer = style::centerrect(r, inner.marginsAdded(st::paidTagPadding));
const auto size = outer.size();
const auto radius = std::min(size.width(), size.height()) / 2;
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(st::radialBg);
p.drawRoundedRect(outer, radius, radius);
p.setPen(st::radialFg);
text.draw(p, {
.position = outer.marginsRemoved(st::paidTagPadding).topLeft(),
});
}
} // namespace
class Checkbox {
@@ -327,18 +349,21 @@ Photo::Photo(
MediaOptions options)
: ItemBase(delegate, parent)
, _data(photo)
, _link(std::make_shared<PhotoOpenClickHandler>(
photo,
crl::guard(this, [=](FullMsgId id) {
clearSpoiler();
delegate->openPhoto(photo, id);
}),
parent->fullId()))
, _spoiler(options.spoiler ? std::make_unique<Ui::SpoilerAnimation>([=] {
, _spoiler((options.spoiler || parent->isMediaSensitive())
? std::make_unique<Ui::SpoilerAnimation>([=] {
delegate->repaintItem(this);
}) : nullptr)
})
: nullptr)
, _sensitiveSpoiler(parent->isMediaSensitive() ? 1 : 0)
, _pinned(options.pinned)
, _story(options.story) {
, _story(options.story)
, _link(_sensitiveSpoiler
? HistoryView::MakeSensitiveMediaLink(
std::make_shared<LambdaClickHandler>(crl::guard(this, [=] {
maybeClearSensitiveSpoiler();
})),
parent)
: makeOpenPhotoHandler()) {
if (_data->inlineThumbnailBytes().isEmpty()
&& (_data->hasExact(Data::PhotoSize::Small)
|| _data->hasExact(Data::PhotoSize::Thumbnail))) {
@@ -348,6 +373,16 @@ Photo::Photo(
Photo::~Photo() = default;
ClickHandlerPtr Photo::makeOpenPhotoHandler() {
return std::make_shared<PhotoOpenClickHandler>(
_data,
crl::guard(this, [=](FullMsgId id) {
clearSpoiler();
delegate()->openPhoto(_data, id);
}),
parent()->fullId());
}
void Photo::initDimensions() {
_maxw = 2 * st::overviewPhotoMinSize;
_minh = _story ? qRound(_maxw * kStoryRatio) : _maxw;
@@ -401,6 +436,10 @@ void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const
QRect(0, 0, _width, _height),
Ui::DefaultImageSpoiler().frame(
_spoiler->index(context->ms, paused)));
if (_sensitiveSpoiler) {
PaintSensitiveTag(p, QRect(0, 0, _width, _height));
}
}
if (selected) {
@@ -453,11 +492,19 @@ void Photo::ensureDataMediaCreated() const {
void Photo::clearSpoiler() {
if (_spoiler) {
_spoiler = nullptr;
_sensitiveSpoiler = false;
_pix = QPixmap();
delegate()->repaintItem(this);
}
}
void Photo::maybeClearSensitiveSpoiler() {
if (_sensitiveSpoiler) {
clearSpoiler();
_link = makeOpenPhotoHandler();
}
}
void Photo::itemDataChanged() {
const auto pinned = parent()->isPinned();
if (_pinned != pinned) {
@@ -488,12 +535,23 @@ Video::Video(
, _data(video)
, _videoCover(LookupVideoCover(video, parent))
, _duration(Ui::FormatDurationText(_data->duration() / 1000))
, _spoiler(options.spoiler ? std::make_unique<Ui::SpoilerAnimation>([=] {
, _spoiler((options.spoiler || parent->isMediaSensitive())
? std::make_unique<Ui::SpoilerAnimation>([=] {
delegate->repaintItem(this);
}) : nullptr)
})
: nullptr)
, _sensitiveSpoiler(parent->isMediaSensitive() ? 1 : 0)
, _pinned(options.pinned)
, _story(options.story) {
setDocumentLinks(_data);
if (_sensitiveSpoiler) {
_openl = HistoryView::MakeSensitiveMediaLink(
std::make_shared<LambdaClickHandler>(crl::guard(this, [=] {
clearSpoiler();
setDocumentLinks(_data);
})),
parent);
}
if (!_videoCover) {
_data->loadThumbnail(parent->fullId());
} else if (_videoCover->inlineThumbnailBytes().isEmpty()
@@ -574,6 +632,10 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const
QRect(0, 0, _width, _height),
Ui::DefaultImageSpoiler().frame(
_spoiler->index(context->ms, paused)));
if (_sensitiveSpoiler) {
PaintSensitiveTag(p, QRect(0, 0, _width, _height));
}
}
if (selected) {
@@ -660,11 +722,19 @@ void Video::ensureDataMediaCreated() const {
void Video::clearSpoiler() {
if (_spoiler) {
_spoiler = nullptr;
_sensitiveSpoiler = false;
_pix = QPixmap();
delegate()->repaintItem(this);
}
}
void Video::maybeClearSensitiveSpoiler() {
if (_sensitiveSpoiler) {
clearSpoiler();
setDocumentLinks(_data);
}
}
void Video::itemDataChanged() {
const auto pinned = parent()->isPinned();
if (_pinned != pinned) {
@@ -700,7 +770,9 @@ TextState Video::getState(
StateRequest request) const {
if (hasPoint(point)) {
ensureDataMediaCreated();
const auto link = (_data->loading() || _data->uploading())
const auto link = _sensitiveSpoiler
? _openl
: (_data->loading() || _data->uploading())
? _cancell
: (dataLoaded() || _dataMedia->canBePlayed(parent()))
? _openl
@@ -2036,8 +2108,22 @@ Gif::Gif(
not_null<HistoryItem*> parent,
not_null<DocumentData*> gif)
: RadialProgressItem(delegate, parent)
, _data(gif) {
, _data(gif)
, _spoiler(parent->isMediaSensitive()
? std::make_unique<Ui::SpoilerAnimation>([=] {
delegate->repaintItem(this);
})
: nullptr)
, _sensitiveSpoiler(parent->isMediaSensitive() ? 1 : 0) {
setDocumentLinks(_data, true);
if (_sensitiveSpoiler) {
_openl = HistoryView::MakeSensitiveMediaLink(
std::make_shared<LambdaClickHandler>(crl::guard(this, [=] {
clearSpoiler();
setDocumentLinks(_data, true);
})),
parent);
}
_data->loadThumbnail(parent->fullId());
}
@@ -2140,6 +2226,23 @@ void Gif::clipCallback(Media::Clip::Notification notification) {
}
}
void Gif::clearSpoiler() {
if (_spoiler) {
_spoiler = nullptr;
_sensitiveSpoiler = false;
_thumb = QImage();
_thumbGood = false;
delegate()->repaintItem(this);
}
}
void Gif::maybeClearSensitiveSpoiler() {
if (_sensitiveSpoiler) {
clearSpoiler();
setDocumentLinks(_data);
}
}
void Gif::validateThumbnail(
Image *image,
QSize size,
@@ -2165,7 +2268,9 @@ void Gif::prepareThumbnail(QSize size, QSize frame) {
Assert(document != nullptr);
ensureDataMediaCreated();
if (!_spoiler) {
validateThumbnail(_dataMedia->thumbnail(), size, frame, true);
}
validateThumbnail(_dataMedia->thumbnailInline(), size, frame, false);
}
@@ -2194,7 +2299,7 @@ void Gif::paint(
});
}
const auto animating = (_gif && _gif->started());
const auto animating = !_spoiler && (_gif && _gif->started());
if (displayLoading) {
ensureRadial();
if (!_radial->animating()) {
@@ -2224,6 +2329,19 @@ void Gif::paint(
}
}
if (_spoiler) {
const auto paused = context->paused || On(PowerSaving::kChatSpoiler);
Ui::FillSpoilerRect(
p,
r,
Ui::DefaultImageSpoiler().frame(
_spoiler->index(context->ms, paused)));
if (_sensitiveSpoiler) {
PaintSensitiveTag(p, r);
}
}
const auto selected = (selection == FullSelection);
if (radial
@@ -76,6 +76,9 @@ public:
virtual void clearHeavyPart() {
}
virtual void maybeClearSensitiveSpoiler() {
}
protected:
[[nodiscard]] not_null<HistoryItem*> parent() const {
return _parent;
@@ -210,21 +213,26 @@ public:
void itemDataChanged() override;
void clearHeavyPart() override;
void maybeClearSensitiveSpoiler() override;
private:
void ensureDataMediaCreated() const;
void setPixFrom(not_null<Image*> image);
[[nodiscard]] ClickHandlerPtr makeOpenPhotoHandler();
void clearSpoiler();
const not_null<PhotoData*> _data;
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
ClickHandlerPtr _link;
std::unique_ptr<Ui::SpoilerAnimation> _spoiler;
QPixmap _pix;
bool _goodLoaded = false;
bool _sensitiveSpoiler = false;
bool _pinned = false;
bool _story = false;
ClickHandlerPtr _link;
};
class Gif final : public RadialProgressItem {
@@ -249,6 +257,9 @@ public:
void clearHeavyPart() override;
void setPosition(int32 position) override;
void clearSpoiler() override;
void maybeClearSensitiveSpoiler() override;
protected:
float64 dataProgress() const override;
bool dataFinished() const override;
@@ -279,9 +290,11 @@ private:
const not_null<DocumentData*> _data;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
StatusText _status;
std::unique_ptr<Ui::SpoilerAnimation> _spoiler;
QImage _thumb;
bool _thumbGood = false;
bool _sensitiveSpoiler = false;
};
@@ -305,6 +318,8 @@ public:
void clearHeavyPart() override;
void clearSpoiler() override;
void maybeClearSensitiveSpoiler() override;
protected:
float64 dataProgress() const override;
bool dataFinished() const override;
@@ -326,6 +341,7 @@ private:
QPixmap _pix;
bool _pixBlurred = true;
bool _sensitiveSpoiler = false;
bool _pinned = false;
bool _story = false;