feat: improve message shot

Add Show Spoiler
Fix photo not showing date in shot
Fix show date in media group
Hide inapplicable options

Co-authored-by: AlexeyZavar <sltkval1@gmail.com>
This commit is contained in:
People-11
2026-03-27 02:35:01 +10:30
committed by GitHub
parent a3d03a3c63
commit b185900b4f
20 changed files with 189 additions and 35 deletions
+1
View File
@@ -8080,6 +8080,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"ayu_MessageShotShowDate" = "Show date";
"ayu_MessageShotShowReactions" = "Show reactions";
"ayu_MessageShotShowColorfulReplies" = "Show colorful replies";
"ayu_MessageShotRevealSpoilers" = "Reveal spoilers";
"ayu_SendAsSticker" = "Send as Sticker";
"ayu_SendWithSound" = "Send With Sound";
"ayu_AyuForwardStatusPreparing" = "Forwarding messages";
+9 -1
View File
@@ -220,6 +220,12 @@ void MessageShotSettings::setShowColorfulReplies(bool val) {
AyuSettings::save();
}
void MessageShotSettings::setRevealSpoilers(bool val) {
if (_revealSpoilers.current() == val) return;
_revealSpoilers = val;
AyuSettings::save();
}
bool MessageShotSettings::isCloudThemeEmpty() const {
return !_cloudThemeId.current()
&& !_cloudThemeAccessHash.current()
@@ -285,6 +291,7 @@ void to_json(nlohmann::json &j, const MessageShotSettings &s) {
{"showDate", s._showDate.current()},
{"showReactions", s._showReactions.current()},
{"showColorfulReplies", s._showColorfulReplies.current()},
{"revealSpoilers", s._revealSpoilers.current()},
{"embeddedThemeType", s._embeddedThemeType.current()},
{"embeddedThemeAccentColor", s._embeddedThemeAccentColor.current()},
{"cloudThemeId", s._cloudThemeId.current()},
@@ -299,7 +306,8 @@ void from_json(const nlohmann::json &j, MessageShotSettings &s) {
s._showBackground = j.value("showBackground", true);
s._showDate = j.value("showDate", false);
s._showReactions = j.value("showReactions", false);
s._showColorfulReplies = j.value("showColorfulReplies", false);
s._showColorfulReplies = j.value("showColorfulReplies", true);
s._revealSpoilers = j.value("revealSpoilers", true);
s._embeddedThemeType = j.value("embeddedThemeType", j.value("themeType", -1));
s._embeddedThemeAccentColor = j.value("embeddedThemeAccentColor", j.value("themeAccentColor", uint32(0)));
s._cloudThemeId = j.value("cloudThemeId", uint64(0));
+4 -1
View File
@@ -167,6 +167,7 @@ public:
[[nodiscard]] bool showDate() const { return _showDate.current(); }
[[nodiscard]] bool showReactions() const { return _showReactions.current(); }
[[nodiscard]] bool showColorfulReplies() const { return _showColorfulReplies.current(); }
[[nodiscard]] bool revealSpoilers() const { return _revealSpoilers.current(); }
[[nodiscard]] int embeddedThemeType() const { return _embeddedThemeType.current(); }
[[nodiscard]] uint32 embeddedThemeAccentColor() const { return _embeddedThemeAccentColor.current(); }
[[nodiscard]] uint64 cloudThemeId() const { return _cloudThemeId.current(); }
@@ -179,6 +180,7 @@ public:
void setShowDate(bool val);
void setShowReactions(bool val);
void setShowColorfulReplies(bool val);
void setRevealSpoilers(bool val);
void setEmbeddedTheme(int type, uint32 accentColor = 0);
void setCloudTheme(uint64 accountId, uint64 id, uint64 accessHash, uint64 documentId, const QString &title);
@@ -195,7 +197,8 @@ private:
rpl::variable<bool> _showBackground = true;
rpl::variable<bool> _showDate = false;
rpl::variable<bool> _showReactions = false;
rpl::variable<bool> _showColorfulReplies = false;
rpl::variable<bool> _showColorfulReplies = true;
rpl::variable<bool> _revealSpoilers = true;
rpl::variable<int> _embeddedThemeType = -1;
rpl::variable<uint32> _embeddedThemeAccentColor = 0;
@@ -315,6 +315,9 @@ void Make(not_null<QWidget*> box, const ShotConfig &config, const Fn<void(QImage
view->itemDataChanged(); // refresh reactions
height += view->resizeGetHeight(width);
if (AyuSettings::getInstance().messageShotSettings().revealSpoilers()) {
view->revealSpoilers();
}
}
width *= style::DevicePixelRatio();
@@ -12,6 +12,8 @@
#include "ayu/ui/components/image_view.h"
#include "ayu/utils/telegram_helpers.h"
#include "boxes/abstract_box.h"
#include "data/data_todo_list.h"
#include "history/history_item.h"
#include "main/main_session.h"
#include "settings/settings_common.h"
#include "styles/style_ayu_styles.h"
@@ -83,6 +85,55 @@ void MessageShotBox::setupContent() {
AddSkip(content);
AddSubsectionTitle(content, tr::ayu_MessageShotPreferences());
auto hasReactions = false;
auto hasReplies = false;
auto hasSpoilers = false;
for (const auto &item : _config.messages) {
if (!hasReactions && !item->reactions().empty()) {
hasReactions = true;
}
if (!hasReplies) {
if (item->replyTo().replying()
|| (item->media() && item->media()->webpage())) {
hasReplies = true;
}
}
if (!hasSpoilers) {
for (const auto &entity : item->originalText().entities) {
if (entity.type() == EntityType::Spoiler) {
hasSpoilers = true;
break;
}
}
if (!hasSpoilers && item->media()) {
if (item->media()->hasSpoiler()) {
hasSpoilers = true;
} else if (const auto todoList = item->media()->todolist()) {
for (const auto &entity : todoList->title.entities) {
if (entity.type() == EntityType::Spoiler) {
hasSpoilers = true;
break;
}
}
if (!hasSpoilers) {
for (const auto &task : todoList->items) {
for (const auto &entity : task.text.entities) {
if (entity.type() == EntityType::Spoiler) {
hasSpoilers = true;
break;
}
}
if (hasSpoilers) break;
}
}
}
}
}
if (hasReactions && hasReplies && hasSpoilers) {
break;
}
}
const auto firstPreviewLatch = std::make_shared<TimedCountDownLatch>(1);
const auto generation = content->lifetime().make_state<int>(0);
const auto weak = base::make_weak(this);
@@ -187,11 +238,12 @@ void MessageShotBox::setupContent() {
},
content->lifetime());
AddButtonWithIcon(
auto latestToggle = AddButtonWithIcon(
content,
tr::ayu_MessageShotShowDate(),
st::settingsButtonNoIcon
)->toggleOn(rpl::single(shotSettings.showDate())
);
latestToggle->toggleOn(rpl::single(shotSettings.showDate())
)->toggledValue(
) | rpl::skip(1) | on_next(
[=](bool enabled)
@@ -201,38 +253,60 @@ void MessageShotBox::setupContent() {
},
content->lifetime());
AddButtonWithIcon(
content,
tr::ayu_MessageShotShowReactions(),
st::settingsButtonNoIcon
)->toggleOn(rpl::single(shotSettings.showReactions())
)->toggledValue(
) | rpl::skip(1) | on_next(
[=](bool enabled)
{
AyuSettings::getInstance().messageShotSettings().setShowReactions(enabled);
updatePreview();
},
content->lifetime());
if (hasReactions) {
latestToggle = AddButtonWithIcon(
content,
tr::ayu_MessageShotShowReactions(),
st::settingsButtonNoIcon
);
latestToggle->toggleOn(rpl::single(shotSettings.showReactions())
)->toggledValue(
) | rpl::skip(1) | on_next(
[=](bool enabled)
{
AyuSettings::getInstance().messageShotSettings().setShowReactions(enabled);
updatePreview();
},
content->lifetime());
}
const auto latestToggle = AddButtonWithIcon(
content,
tr::ayu_MessageShotShowColorfulReplies(),
st::settingsButtonNoIcon
);
latestToggle->toggleOn(rpl::single(shotSettings.showColorfulReplies())
)->toggledValue(
) | rpl::skip(1) | on_next(
[=](bool enabled)
{
auto &currentSettings = AyuSettings::getInstance();
currentSettings.messageShotSettings().setShowColorfulReplies(enabled);
currentSettings.setSimpleQuotesAndReplies(!enabled);
if (hasReplies) {
latestToggle = AddButtonWithIcon(
content,
tr::ayu_MessageShotShowColorfulReplies(),
st::settingsButtonNoIcon
);
latestToggle->toggleOn(rpl::single(shotSettings.showColorfulReplies())
)->toggledValue(
) | rpl::skip(1) | on_next(
[=](bool enabled)
{
auto &currentSettings = AyuSettings::getInstance();
currentSettings.messageShotSettings().setShowColorfulReplies(enabled);
currentSettings.setSimpleQuotesAndReplies(!enabled);
_config.st = std::make_shared<Ui::ChatStyle>(_config.st.get());
updatePreview();
},
content->lifetime());
_config.st = std::make_shared<Ui::ChatStyle>(_config.st.get());
updatePreview();
},
content->lifetime());
}
if (hasSpoilers) {
latestToggle = AddButtonWithIcon(
content,
tr::ayu_MessageShotRevealSpoilers(),
st::settingsButtonNoIcon
);
latestToggle->toggleOn(rpl::single(shotSettings.revealSpoilers())
)->toggledValue(
) | rpl::skip(1) | on_next(
[=](bool enabled)
{
AyuSettings::getInstance().messageShotSettings().setRevealSpoilers(enabled);
updatePreview();
},
content->lifetime());
}
AddSkip(content);
@@ -1298,6 +1298,15 @@ void Element::hideSpoilers() {
}
}
void Element::revealSpoilers() {
if (_text.hasSpoilers()) {
_text.setSpoilerRevealed(true, anim::type::instant);
}
if (_media) {
_media->revealSpoilers();
}
}
void Element::customEmojiRepaint() {
if (!(_flags & Flag::CustomEmojiRepainting)) {
_flags |= Flag::CustomEmojiRepainting;
@@ -654,6 +654,7 @@ public:
const Reactions::InlineList &reactions) const;
void clearCustomEmojiRepaint() const;
void hideSpoilers();
void revealSpoilers();
void repaint(QRect r = QRect()) const;
[[nodiscard]] ClickHandlerPtr fromPhotoLink() const {
@@ -1738,6 +1738,12 @@ void Document::hideSpoilers() {
}
}
void Document::revealSpoilers() {
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
captioned->caption.setSpoilerRevealed(true, anim::type::instant);
}
}
Ui::Text::String Document::createCaption() const {
return File::createCaption(_realParent);
}
@@ -61,6 +61,7 @@ public:
}
void hideSpoilers() override;
void revealSpoilers() override;
bool needsBubble() const override {
return true;
}
@@ -1701,6 +1701,12 @@ void Gif::hideSpoilers() {
}
}
void Gif::revealSpoilers() {
if (_spoiler) {
_spoiler->revealed = true;
}
}
bool Gif::needsBubble() const {
if (_storyId) {
return true;
@@ -102,6 +102,7 @@ public:
QImage spoilerTagBackground() const override;
void hideSpoilers() override;
void revealSpoilers() override;
bool needsBubble() const override;
bool unwrapped() const override;
bool customInfoLayout() const override {
@@ -265,6 +265,8 @@ public:
virtual void hideSpoilers() {
}
virtual void revealSpoilers() {
}
[[nodiscard]] virtual bool needsBubble() const = 0;
[[nodiscard]] virtual bool unwrapped() const {
return false;
@@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
// AyuGram includes
#include "ayu/ayu_settings.h"
#include "ayu/features/message_shot/message_shot.h"
namespace HistoryView {
@@ -512,7 +513,7 @@ void GroupedMedia::draw(Painter &p, const PaintContext &context) const {
if (_parent->media() == this && (!_parent->hasBubble() || isBubbleBottom())) {
auto fullRight = width();
auto fullBottom = height();
if (needInfoDisplay()) {
if (needInfoDisplay() && !AyuFeatures::MessageShot::ignoreRender(AyuFeatures::MessageShot::RenderPart::Date)) {
_parent->drawInfo(
p,
context,
@@ -847,6 +848,12 @@ void GroupedMedia::hideSpoilers() {
}
}
void GroupedMedia::revealSpoilers() {
for (const auto &part : _parts) {
part.content->revealSpoilers();
}
}
Storage::SharedMediaTypesMask GroupedMedia::sharedMediaTypes() const {
return main()->sharedMediaTypes();
}
@@ -960,6 +967,9 @@ bool GroupedMedia::computeNeedBubble() const {
}
bool GroupedMedia::needInfoDisplay() const {
if (AyuFeatures::MessageShot::isTakingShot()) {
return (_mode != Mode::Column);
}
const auto item = _parent->data();
return (_mode != Mode::Column)
&& (item->isSending()
@@ -74,6 +74,7 @@ public:
bool pressed) override;
void hideSpoilers() override;
void revealSpoilers() override;
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
bool overrideEditedDate() const override {
@@ -893,6 +893,10 @@ bool Photo::needInfoDisplay() const {
return false;
}
if (AyuFeatures::MessageShot::isTakingShot()) {
return true;
}
if (_parent->data()->isFakeAboutView()) {
return false;
}
@@ -1094,6 +1098,12 @@ void Photo::hideSpoilers() {
}
}
void Photo::revealSpoilers() {
if (_spoiler) {
_spoiler->revealed = true;
}
}
bool Photo::needsBubble() const {
if (_storyId) {
return true;
@@ -84,6 +84,7 @@ public:
QImage spoilerTagBackground() const override;
void hideSpoilers() override;
void revealSpoilers() override;
bool needsBubble() const override;
bool customInfoLayout() const override {
return true;
@@ -422,6 +422,10 @@ void ServiceBox::hideSpoilers() {
_subtitle.setSpoilerRevealed(false, anim::type::instant);
}
void ServiceBox::revealSpoilers() {
_subtitle.setSpoilerRevealed(true, anim::type::instant);
}
bool ServiceBox::hasHeavyPart() const {
return _content->hasHeavyPart();
}
@@ -103,6 +103,7 @@ public:
return _content->hideServiceText();
}
void hideSpoilers() override;
void revealSpoilers() override;
bool hasHeavyPart() const override;
void unloadHeavyPart() override;
@@ -864,6 +864,17 @@ void TodoList::hideSpoilers() {
}
}
void TodoList::revealSpoilers() {
if (_title.hasSpoilers()) {
_title.setSpoilerRevealed(true, anim::type::instant);
}
for (auto &task : _tasks) {
if (task.text.hasSpoilers()) {
task.text.setSpoilerRevealed(true, anim::type::instant);
}
}
}
std::vector<Media::TodoTaskInfo> TodoList::takeTasksInfo() {
if (_tasks.empty()) {
return {};
@@ -65,6 +65,7 @@ public:
bool hasHeavyPart() const override;
void hideSpoilers() override;
void revealSpoilers() override;
std::vector<TodoTaskInfo> takeTasksInfo() override;