diff --git a/Telegram/Resources/animations/voice_ttl_idle.tgs b/Telegram/Resources/animations/voice_ttl_idle.tgs
new file mode 100644
index 000000000..016611298
Binary files /dev/null and b/Telegram/Resources/animations/voice_ttl_idle.tgs differ
diff --git a/Telegram/Resources/animations/voice_ttl_start.tgs b/Telegram/Resources/animations/voice_ttl_start.tgs
new file mode 100644
index 000000000..835556dd0
Binary files /dev/null and b/Telegram/Resources/animations/voice_ttl_start.tgs differ
diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc
index 705b508a2..76ea1ef4a 100644
--- a/Telegram/Resources/qrc/telegram/animations.qrc
+++ b/Telegram/Resources/qrc/telegram/animations.qrc
@@ -11,5 +11,7 @@
../../animations/ttl.tgs
../../animations/discussion.tgs
../../animations/stats.tgs
+ ../../animations/voice_ttl_idle.tgs
+ ../../animations/voice_ttl_start.tgs
diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp
index 71e2929ac..e9c4dd66e 100644
--- a/Telegram/SourceFiles/data/data_media_types.cpp
+++ b/Telegram/SourceFiles/data/data_media_types.cpp
@@ -1140,6 +1140,10 @@ crl::time MediaFile::ttlSeconds() const {
return _ttlSeconds;
}
+bool MediaFile::allowsForward() const {
+ return !ttlSeconds();
+}
+
bool MediaFile::updateInlineResultMedia(const MTPMessageMedia &media) {
if (media.type() != mtpc_messageMediaDocument) {
return false;
diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h
index 27d1718c1..b2323af8a 100644
--- a/Telegram/SourceFiles/data/data_media_types.h
+++ b/Telegram/SourceFiles/data/data_media_types.h
@@ -281,6 +281,7 @@ public:
bool dropForwardedInfo() const override;
bool hasSpoiler() const override;
crl::time ttlSeconds() const override;
+ bool allowsForward() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp
index df9f0f751..f1d273d63 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/random.h"
#include "lang/lang_keys.h"
+#include "lottie/lottie_icon.h"
#include "storage/localstorage.h"
#include "main/main_session.h"
#include "media/player/media_player_float.h" // Media::Player::RoundPainter.
@@ -31,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/cached_round_corners.h"
#include "ui/painter.h"
#include "ui/power_saving.h"
+#include "ui/rect.h"
#include "ui/ui_utility.h"
#include "data/data_session.h"
#include "data/data_document.h"
@@ -48,6 +50,52 @@ namespace {
constexpr auto kAudioVoiceMsgUpdateView = crl::time(100);
+[[nodiscard]] HistoryView::TtlPaintCallback CreateTtlPaintCallback(
+ std::shared_ptr lifetime,
+ Fn update) {
+ struct State final {
+ std::unique_ptr start;
+ std::unique_ptr idle;
+ };
+ const auto iconSize = Size(std::min(
+ st::historyFileInPause.width(),
+ st::historyFileInPause.height()));
+ const auto state = lifetime->make_state();
+ state->start = Lottie::MakeIcon({
+ .name = u"voice_ttl_start"_q,
+ .color = &st::historyFileInIconFg,
+ .sizeOverride = iconSize,
+ });
+
+ const auto animateSingle = [=](
+ not_null icon,
+ Fn next) {
+ auto callback = [=] {
+ update();
+ if (icon->frameIndex() == icon->framesCount()) {
+ next();
+ }
+ };
+ icon->animate(std::move(callback), 0, icon->framesCount());
+ };
+ const auto animate = [=](auto reanimate) -> void {
+ animateSingle(state->idle.get(), [=] { reanimate(reanimate); });
+ };
+ animateSingle(
+ state->start.get(),
+ [=] {
+ state->idle = Lottie::MakeIcon({
+ .name = u"voice_ttl_idle"_q,
+ .color = &st::historyFileInIconFg,
+ .sizeOverride = iconSize,
+ });
+ animate(animate);
+ });
+ return [=](QPainter &p, QRect r, QColor c) {
+ (state->idle ? state->idle : state->start)->paintInCenter(p, r, c);
+ };
+}
+
[[nodiscard]] bool OncePlayable(not_null item) {
return !item->out() && item->media()->ttlSeconds();
}
@@ -249,9 +297,11 @@ Document::Document(
Ui::Text::WithEntities)
});
if (lifetime) {
+ _drawTtl = nullptr;
base::take(lifetime)->destroy();
}
}, *lifetime);
+ _drawTtl = CreateTtlPaintCallback(lifetime, [=] { repaint(); });
return false;
});
@@ -670,7 +720,9 @@ void Document::draw(
: nullptr;
const auto paintContent = [&](QPainter &q) {
- if (previous && radialOpacity > 0. && radialOpacity < 1.) {
+ if (_drawTtl) {
+ _drawTtl(q, inner, context.st->historyFileInIconFg()->c);
+ } else if (previous && radialOpacity > 0. && radialOpacity < 1.) {
PaintInterpolatedIcon(q, icon, *previous, radialOpacity, inner);
} else {
icon.paintInCenter(q, inner);
diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.h b/Telegram/SourceFiles/history/view/media/history_view_document.h
index 126a7c777..5e57308ed 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_document.h
+++ b/Telegram/SourceFiles/history/view/media/history_view_document.h
@@ -25,6 +25,8 @@ class String;
namespace HistoryView {
+using TtlPaintCallback = Fn;
+
class Document final
: public File
, public RuntimeComposer {
@@ -178,6 +180,8 @@ private:
mutable TooltipFilename _tooltipFilename;
+ TtlPaintCallback _drawTtl;
+
bool _transcribedRound = false;
};