diff --git a/Telegram/Resources/art/ttl/video_message_icon.svg b/Telegram/Resources/art/ttl/video_message_icon.svg new file mode 100644 index 000000000..aeadc8385 --- /dev/null +++ b/Telegram/Resources/art/ttl/video_message_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Telegram/Resources/icons/chat/audio_once.png b/Telegram/Resources/icons/chat/audio_once.png new file mode 100644 index 000000000..4d1f93652 Binary files /dev/null and b/Telegram/Resources/icons/chat/audio_once.png differ diff --git a/Telegram/Resources/icons/chat/audio_once@2x.png b/Telegram/Resources/icons/chat/audio_once@2x.png new file mode 100644 index 000000000..f66cfbbcb Binary files /dev/null and b/Telegram/Resources/icons/chat/audio_once@2x.png differ diff --git a/Telegram/Resources/icons/chat/audio_once@3x.png b/Telegram/Resources/icons/chat/audio_once@3x.png new file mode 100644 index 000000000..bce2481c5 Binary files /dev/null and b/Telegram/Resources/icons/chat/audio_once@3x.png differ diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc index 57afe3882..ccf1a1d7a 100644 --- a/Telegram/Resources/qrc/telegram/telegram.qrc +++ b/Telegram/Resources/qrc/telegram/telegram.qrc @@ -26,6 +26,7 @@ ../../art/recording/recording_info_audio.svg ../../art/recording/recording_info_video_landscape.svg ../../art/recording/recording_info_video_portrait.svg + ../../art/ttl/video_message_icon.svg ../../icons/settings/dino.svg ../../icons/settings/star.svg ../../icons/settings/starmini.svg diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index 4a29e4e93..a249195c9 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -808,7 +808,3 @@ void ClearMediaAsExpired(not_null item) { } } } - -[[nodiscard]] bool IsVoiceOncePlayable(not_null item) { - return !item->out() && item->media()->ttlSeconds(); -} diff --git a/Telegram/SourceFiles/history/history_item_helpers.h b/Telegram/SourceFiles/history/history_item_helpers.h index f96d9c092..c438eccbe 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.h +++ b/Telegram/SourceFiles/history/history_item_helpers.h @@ -158,4 +158,3 @@ ClickHandlerPtr JumpToStoryClickHandler( void ShowTrialTranscribesToast(int left, TimeId until); void ClearMediaAsExpired(not_null item); -[[nodiscard]] bool IsVoiceOncePlayable(not_null item); diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 32763b683..cf5560009 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -425,7 +425,7 @@ void Document::createComponents(bool caption) { _realParent->fullId()); } if (const auto voice = Get()) { - voice->seekl = !IsVoiceOncePlayable(_parent->data()) + voice->seekl = !_parent->data()->media()->ttlSeconds() ? std::make_shared(_data, [](FullMsgId) {}) : nullptr; if (_transcribedRound) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 87a0e00bf..6176bf050 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/view/media_view_playback_progress.h" #include "ui/boxes/confirm_box.h" #include "ui/painter.h" +#include "ui/rect.h" #include "history/history_item_components.h" #include "history/history_item_helpers.h" #include "history/history_item.h" @@ -30,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" #include "history/view/history_view_reply.h" #include "history/view/history_view_transcribe_button.h" +#include "history/view/media/history_view_document.h" // TTLVoiceStops #include "history/view/media/history_view_media_common.h" #include "history/view/media/history_view_media_spoiler.h" #include "window/window_session_controller.h" @@ -52,6 +54,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document_media.h" #include "styles/style_chat.h" +#include + namespace HistoryView { namespace { @@ -65,6 +69,41 @@ int gifMaxStatusWidth(DocumentData *document) { return result; } +[[nodiscard]] HistoryView::TtlRoundPaintCallback CreateTtlPaintCallback( + Fn update) { + const auto iconSize = Size(std::min( + st::historyFileInPause.width(), + st::historyFileInPause.height())); + const auto centerMargins = Margins(st::historyFileInPause.width() * 3); + + const auto renderer = std::make_shared( + u":/gui/ttl/video_message_icon.svg"_q); + + return [=](QPainter &p, QRect r, const PaintContext &context) { + const auto centerRect = r - centerMargins; + const auto &icon = context.imageStyle()->historyVideoMessageTtlIcon; + const auto iconRect = QRect( + rect::right(centerRect) - icon.width() * 0.75, + rect::bottom(centerRect) - icon.height() * 0.75, + icon.width(), + icon.height()); + { + auto hq = PainterHighQualityEnabler(p); + auto path = QPainterPath(); + path.setFillRule(Qt::WindingFill); + path.addEllipse(centerRect); + path.addEllipse(iconRect); + p.fillPath(path, st::shadowFg); + p.fillPath(path, st::shadowFg); + p.fillPath(path, st::shadowFg); + } + + renderer->render(&p, centerRect - Margins(centerRect.width() / 4)); + + icon.paint(p, iconRect.topLeft(), centerRect.width()); + }; +} + } // namespace struct Gif::Streamed { @@ -83,6 +122,12 @@ Gif::Streamed::Streamed( : instance(std::move(shared), std::move(waitingCallback)) { } +[[nodiscard]] bool IsHiddenRoundMessage(not_null parent) { + return parent->delegate()->elementContext() != Context::TTLViewer + && parent->data()->media() + && parent->data()->media()->ttlSeconds(); +} + Gif::Gif( not_null parent, not_null realParent, @@ -95,18 +140,45 @@ Gif::Gif( : FullStoryId()) , _caption( st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) -, _spoiler(spoiler ? std::make_unique() : nullptr) +, _spoiler((spoiler || IsHiddenRoundMessage(_parent)) + ? std::make_unique() + : nullptr) , _downloadSize(Ui::FormatSizeText(_data->size)) { - setDocumentLinks(_data, realParent, [=] { - if (!_data->createMediaView()->canBePlayed(realParent) - || !_data->isAnimation() - || _data->isVideoMessage() - || !CanPlayInline(_data)) { - return false; + auto hasDefaultDocumentLinks = false; + if (_data->isVideoMessage() && _parent->data()->media()->ttlSeconds()) { + if (_spoiler) { + _drawTtl = CreateTtlPaintCallback([=] { repaint(); }); } - playAnimation(false); - return true; - }); + const auto fullId = _realParent->fullId(); + _parent->data()->removeFromSharedMediaIndex(); + setDocumentLinks(_data, realParent, [=] { + auto lifetime = std::make_shared(); + TTLVoiceStops(fullId) | rpl::start_with_next([=]() mutable { + const auto item = _parent->data(); + if (lifetime) { + base::take(lifetime)->destroy(); + } + if (!item->out()) { + // Destroys this. + ClearMediaAsExpired(item); + } + }, *lifetime); + + return false; + }); + } else { + setDocumentLinks(_data, realParent, [=] { + if (!_data->createMediaView()->canBePlayed(realParent) + || !_data->isAnimation() + || _data->isVideoMessage() + || !CanPlayInline(_data)) { + return false; + } + playAnimation(false); + return true; + }); + } + setStatusSize(Ui::FileStatusSizeReady); if (_spoiler) { @@ -403,7 +475,13 @@ void Gif::draw(Painter &p, const PaintContext &context) const { QRect rthumb(style::rtlrect(usex + paintx, painty, usew, painth, width())); - const auto revealed = (!isRound && _spoiler) + const auto inTTLViewer = _parent->delegate()->elementContext() + == Context::TTLViewer; + const auto revealed = (isRound + && item->media()->ttlSeconds() + && !inTTLViewer) + ? 0 + : (!isRound && _spoiler) ? _spoiler->revealAnimation.value(_spoiler->revealed ? 1. : 0.) : 1.; const auto fullHiddenBySpoiler = (revealed == 0.); @@ -525,10 +603,19 @@ void Gif::draw(Painter &p, const PaintContext &context) const { p.drawImage(rthumb, _thumbCache); } - if (!isRound && revealed < 1.) { + if (revealed < 1.) { p.setOpacity(1. - revealed); - p.drawImage(rthumb.topLeft(), _spoiler->background); - fillImageSpoiler(p, _spoiler.get(), rthumb, context); + if (!isRound) { + p.drawImage(rthumb.topLeft(), _spoiler->background); + fillImageSpoiler(p, _spoiler.get(), rthumb, context); + } else { + auto frame = _spoiler->background; + { + auto q = QPainter(&frame); + fillImageSpoiler(q, _spoiler.get(), rthumb, context); + } + p.drawImage(rthumb.topLeft(), Images::Circle(std::move(frame))); + } p.setOpacity(1.); } if (context.selected()) { @@ -793,6 +880,9 @@ void Gif::draw(Painter &p, const PaintContext &context) const { paintTranscribe(p, usex, fullBottom, false, context); } } + if (_drawTtl) { + _drawTtl(p, rthumb, context); + } } void Gif::paintTranscribe( @@ -1108,7 +1198,9 @@ TextState Gif::textState(QPoint point, StateRequest request) const { } if (QRect(usex + paintx, painty, usew, painth).contains(point)) { ensureDataMediaCreated(); - result.link = (_spoiler && !_spoiler->revealed) + result.link = (isRound && _parent->data()->media()->ttlSeconds()) + ? _openl // Overriden. + : (_spoiler && !_spoiler->revealed) ? _spoiler->link : _data->uploading() ? _cancell @@ -1970,7 +2062,7 @@ bool Gif::needCornerStatusDisplay() const { void Gif::ensureTranscribeButton() const { if (_data->isVideoMessage() - && !IsVoiceOncePlayable(_parent->data()) + && !_parent->data()->media()->ttlSeconds() && (_data->session().premium() || _data->session().api().transcribes().trialsSupport())) { if (!_transcribe) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.h b/Telegram/SourceFiles/history/view/media/history_view_gif.h index 1d1b30e4a..dad49b351 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.h +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.h @@ -40,6 +40,11 @@ namespace HistoryView { class Reply; class TranscribeButton; +using TtlRoundPaintCallback = Fn; + class Gif final : public File { public: Gif( @@ -214,6 +219,8 @@ private: void togglePollingStory(bool enabled) const; + TtlRoundPaintCallback _drawTtl; + const not_null _data; const FullStoryId _storyId; Ui::Text::String _caption; diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index bc54f7366..378393643 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -536,6 +536,8 @@ historyVideoMessageMute: icon {{ "volume_mute", historyFileThumbIconFg }}; historyVideoMessageMuteSelected: icon {{ "volume_mute", historyFileThumbIconFgSelected }}; historyVideoMessageMuteSize: 25px; historyVideoMessageProgressOpacity: 0.72; +historyVideoMessageTtlIcon: icon {{ "chat/audio_once", historyFileThumbIconFg }}; +historyVideoMessageTtlIconSelected: icon {{ "chat/audio_once", historyFileThumbIconFgSelected }}; historyAdminLogEmptyWidth: 260px; historyAdminLogEmptyPadding: margins(10px, 12px, 10px, 12px); diff --git a/Telegram/SourceFiles/ui/chat/chat_style.cpp b/Telegram/SourceFiles/ui/chat/chat_style.cpp index 0fc4840f1..4acf416ba 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_style.cpp @@ -528,6 +528,10 @@ ChatStyle::ChatStyle(rpl::producer colorIndices) { &MessageImageStyle::historyVideoMessageMute, st::historyVideoMessageMute, st::historyVideoMessageMuteSelected); + make( + &MessageImageStyle::historyVideoMessageTtlIcon, + st::historyVideoMessageTtlIcon, + st::historyVideoMessageTtlIconSelected); make( &MessageImageStyle::historyPageEnlarge, st::historyPageEnlarge, diff --git a/Telegram/SourceFiles/ui/chat/chat_style.h b/Telegram/SourceFiles/ui/chat/chat_style.h index 45a4f3caf..11890a061 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.h +++ b/Telegram/SourceFiles/ui/chat/chat_style.h @@ -118,6 +118,7 @@ struct MessageImageStyle { style::icon historyVideoDownload = { Qt::Uninitialized }; style::icon historyVideoCancel = { Qt::Uninitialized }; style::icon historyVideoMessageMute = { Qt::Uninitialized }; + style::icon historyVideoMessageTtlIcon = { Qt::Uninitialized }; style::icon historyPageEnlarge = { Qt::Uninitialized }; };