From ac92e1c99e5800c1b3d0c906e37b51dbab5f24f3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 8 Aug 2024 10:56:31 +0200 Subject: [PATCH] Start paid reaction toast notification. --- Telegram/CMakeLists.txt | 2 + .../animations/star_reaction/toast.tgs | Bin 0 -> 13797 bytes Telegram/Resources/langs/lang.strings | 4 + .../Resources/qrc/telegram/animations.qrc | 1 + .../chat_helpers/stickers_lottie.cpp | 12 +- .../data/data_message_reactions.cpp | 26 ++- .../SourceFiles/data/data_message_reactions.h | 6 + Telegram/SourceFiles/data/data_session.cpp | 8 + Telegram/SourceFiles/data/data_session.h | 3 + Telegram/SourceFiles/history/history_item.cpp | 7 + Telegram/SourceFiles/history/history_item.h | 1 + .../SourceFiles/history/history_widget.cpp | 8 + Telegram/SourceFiles/history/history_widget.h | 3 + .../view/history_view_paid_reaction_toast.cpp | 216 ++++++++++++++++++ .../view/history_view_paid_reaction_toast.h | 68 ++++++ .../payments/payments_reaction_process.cpp | 2 + .../SourceFiles/storage/localimageloader.cpp | 5 +- .../SourceFiles/storage/localimageloader.h | 3 +- Telegram/lib_ui | 2 +- 19 files changed, 371 insertions(+), 6 deletions(-) create mode 100644 Telegram/Resources/animations/star_reaction/toast.tgs create mode 100644 Telegram/SourceFiles/history/view/history_view_paid_reaction_toast.cpp create mode 100644 Telegram/SourceFiles/history/view/history_view_paid_reaction_toast.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 7ee4e538b..cb934282b 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -826,6 +826,8 @@ PRIVATE history/view/history_view_message.cpp history/view/history_view_message.h history/view/history_view_object.h + history/view/history_view_paid_reaction_toast.cpp + history/view/history_view_paid_reaction_toast.h history/view/history_view_pinned_bar.cpp history/view/history_view_pinned_bar.h history/view/history_view_pinned_section.cpp diff --git a/Telegram/Resources/animations/star_reaction/toast.tgs b/Telegram/Resources/animations/star_reaction/toast.tgs new file mode 100644 index 0000000000000000000000000000000000000000..abfb9602c9b4a9cd1f9e99630c117e55d662c1f4 GIT binary patch literal 13797 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 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 13cab0bee..cb07fac5b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3426,6 +3426,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_paid_react_send" = "Send {price}"; "lng_paid_react_agree" = "By sending stars, you agree to the {link}."; "lng_paid_react_agree_link" = "Terms of Service"; +"lng_paid_react_toast_title" = "Star Sent!"; +"lng_paid_react_toast_text#one" = "You reacted with **{count} Star**."; +"lng_paid_react_toast_text#other" = "You reacted with **{count} Stars**."; +"lng_paid_react_undo" = "Undo"; "lng_translate_show_original" = "Show Original"; "lng_translate_bar_to" = "Translate to {name}"; diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc index 6292da0c4..ac6ff1dbf 100644 --- a/Telegram/Resources/qrc/telegram/animations.qrc +++ b/Telegram/Resources/qrc/telegram/animations.qrc @@ -43,6 +43,7 @@ ../../animations/star_reaction/appear.tgs ../../animations/star_reaction/center.tgs ../../animations/star_reaction/select.tgs + ../../animations/star_reaction/toast.tgs ../../animations/star_reaction/effect1.tgs ../../animations/star_reaction/effect2.tgs ../../animations/star_reaction/effect3.tgs diff --git a/Telegram/SourceFiles/chat_helpers/stickers_lottie.cpp b/Telegram/SourceFiles/chat_helpers/stickers_lottie.cpp index 314a99a8a..095abbb57 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_lottie.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_lottie.cpp @@ -24,6 +24,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/painter.h" #include "main/main_session.h" +#include + namespace ChatHelpers { namespace { @@ -315,6 +317,12 @@ QSize ComputeStickerSize(not_null document, QSize box) { return HistoryView::NonEmptySize(request.size(dimensions, 8) / ratio); } +[[nodiscard]] uint64 LocalTgsStickerId(QStringView name) { + auto full = u"local_tgs_sticker:"_q; + full.append(name); + return XXH64(full.data(), full.size() * sizeof(QChar), 0); +} + not_null GenerateLocalTgsSticker( not_null session, const QString &name) { @@ -327,7 +335,9 @@ not_null GenerateLocalTgsSticker( SendMediaType::File, FileLoadTo(0, {}, {}, 0), {}, - false); + false, + nullptr, + LocalTgsStickerId(name)); task.process({ .generateGoodThumbnail = false }); const auto result = task.peekResult(); Assert(result != nullptr); diff --git a/Telegram/SourceFiles/data/data_message_reactions.cpp b/Telegram/SourceFiles/data/data_message_reactions.cpp index ff21d3d05..cf783d1ee 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.cpp +++ b/Telegram/SourceFiles/data/data_message_reactions.cpp @@ -48,7 +48,7 @@ constexpr auto kRecentReactionsLimit = 40; constexpr auto kMyTagsRequestTimeout = crl::time(1000); constexpr auto kTopRequestDelay = 60 * crl::time(1000); constexpr auto kTopReactionsLimit = 14; -constexpr auto kPaidAccumulatePeriod = 5 * crl::time(1000); +constexpr auto kPaidAccumulatePeriod = 5 * crl::time(1000) + 500; [[nodiscard]] QString ReactionIdToLog(const ReactionId &id) { if (const auto custom = id.custom()) { @@ -1525,6 +1525,15 @@ not_null Reactions::lookupPaid() { return &*_paid; } +not_null Reactions::paidToastAnimation() { + if (!_paidToastAnimation) { + _paidToastAnimation = ChatHelpers::GenerateLocalTgsSticker( + &_owner->session(), + u"star_reaction_toast"_q); + } + return _paidToastAnimation; +} + rpl::producer> Reactions::myTagsValue( SavedSublist *sublist) { refreshMyTags(sublist); @@ -1546,6 +1555,21 @@ void Reactions::schedulePaid(not_null item) { } } +void Reactions::undoScheduledPaid(not_null item) { + _sendPaidItems.remove(item); + item->cancelScheduledPaidReaction(); +} + +crl::time Reactions::sendingScheduledPaidAt( + not_null item) const { + const auto i = _sendPaidItems.find(item); + return (i != end(_sendPaidItems)) ? i->second : crl::time(); +} + +crl::time Reactions::ScheduledPaidDelay() { + return kPaidAccumulatePeriod; +} + void Reactions::repaintCollected() { const auto now = crl::now(); auto closest = crl::time(); diff --git a/Telegram/SourceFiles/data/data_message_reactions.h b/Telegram/SourceFiles/data/data_message_reactions.h index 5ab61ac50..bc61c7c4c 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.h +++ b/Telegram/SourceFiles/data/data_message_reactions.h @@ -139,11 +139,16 @@ public: void clearTemporary(); [[nodiscard]] Reaction *lookupTemporary(const ReactionId &id); [[nodiscard]] not_null lookupPaid(); + [[nodiscard]] not_null paidToastAnimation(); [[nodiscard]] rpl::producer> myTagsValue( SavedSublist *sublist = nullptr); void schedulePaid(not_null item); + void undoScheduledPaid(not_null item); + [[nodiscard]] crl::time sendingScheduledPaidAt( + not_null item) const; + [[nodiscard]] static crl::time ScheduledPaidDelay(); [[nodiscard]] static bool HasUnread(const MTPMessageReactions &data); static void CheckUnknownForUnread( @@ -293,6 +298,7 @@ private: // Otherwise we could use flat_map>. std::map _temporary; std::optional _paid; + DocumentData *_paidToastAnimation = nullptr; base::Timer _topRefreshTimer; mtpRequestId _topRequestId = 0; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index a673ebca5..ec4d1c6e4 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1884,6 +1884,14 @@ rpl::producer> Session::viewRemoved() const { return _viewRemoved.events(); } +void Session::notifyViewPaidReactionSent(not_null view) { + _viewPaidReactionSent.fire_copy(view); +} + +rpl::producer> Session::viewPaidReactionSent() const { + return _viewPaidReactionSent.events(); +} + void Session::notifyHistoryUnloaded(not_null history) { _historyUnloaded.fire_copy(history); } diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 06b4b267d..5abb66c98 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -306,6 +306,8 @@ public: [[nodiscard]] rpl::producer> historyCleared() const; void notifyHistoryChangeDelayed(not_null history); [[nodiscard]] rpl::producer> historyChanged() const; + void notifyViewPaidReactionSent(not_null view); + [[nodiscard]] rpl::producer> viewPaidReactionSent() const; void sendHistoryChangeNotifications(); void notifyPinnedDialogsOrderUpdated(); @@ -923,6 +925,7 @@ private: rpl::event_stream> _itemDataChanges; rpl::event_stream> _itemRemoved; rpl::event_stream> _viewRemoved; + rpl::event_stream> _viewPaidReactionSent; rpl::event_stream> _historyUnloaded; rpl::event_stream> _historyCleared; base::flat_set> _historiesChanged; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 69287a9cf..a8e74a4f5 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -2525,6 +2525,13 @@ void HistoryItem::addPaidReaction(int count) { _history->owner().notifyItemDataChange(this); } +void HistoryItem::cancelScheduledPaidReaction() { + if (_reactions) { + _reactions->cancelScheduledPaid(); + _history->owner().notifyItemDataChange(this); + } +} + int HistoryItem::startPaidReactionSending() { return _reactions ? _reactions->startPaidSending() : 0; } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index d9b48dfd5..be5c7f7a9 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -446,6 +446,7 @@ public: const Data::ReactionId &reaction, HistoryReactionSource source); void addPaidReaction(int count); + void cancelScheduledPaidReaction(); [[nodiscard]] int startPaidReactionSending(); void finishPaidReactionSending(int count, bool success); void updateReactionsUnknown(); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 6356344f9..86012f331 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -104,6 +104,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_top_bar_widget.h" #include "history/view/history_view_contact_status.h" #include "history/view/history_view_context_menu.h" +#include "history/view/history_view_paid_reaction_toast.h" #include "history/view/history_view_pinned_tracker.h" #include "history/view/history_view_pinned_section.h" #include "history/view/history_view_pinned_bar.h" @@ -285,6 +286,13 @@ HistoryWidget::HistoryWidget( }) , _saveDraftTimer([=] { saveDraft(); }) , _saveCloudDraftTimer([=] { saveCloudDraft(); }) +, _paidReactionToast(std::make_unique( + this, + &session().data(), + rpl::single(st::topBarHeight), + [=](not_null view) { + return _list && _list->itemTop(view) >= 0; + })) , _topShadow(this) { setAcceptDrops(true); diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index b90ef037d..ef17e3285 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -87,6 +87,7 @@ class TabbedSelector; namespace HistoryView { class StickerToast; +class PaidReactionToast; class TopBarWidget; class ContactStatus; class BusinessBotStatus; @@ -825,6 +826,8 @@ private: std::unique_ptr _stickerToast; std::unique_ptr _chooseForReport; + std::unique_ptr _paidReactionToast; + base::flat_set> _itemRevealPending; base::flat_map< not_null, diff --git a/Telegram/SourceFiles/history/view/history_view_paid_reaction_toast.cpp b/Telegram/SourceFiles/history/view/history_view_paid_reaction_toast.cpp new file mode 100644 index 000000000..a4125a076 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_paid_reaction_toast.cpp @@ -0,0 +1,216 @@ +/* +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/history_view_paid_reaction_toast.h" + +#include "chat_helpers/stickers_lottie.h" +#include "data/data_document.h" +#include "data/data_document_media.h" +#include "data/data_message_reactions.h" +#include "data/data_session.h" +#include "history/view/history_view_element.h" +#include "history/history_item.h" +//#include "main/main_session.h" +#include "lang/lang_keys.h" +#include "ui/text/text_utilities.h" +#include "ui/toast/toast.h" +#include "ui/toast/toast_widget.h" +#include "ui/widgets/buttons.h" +//#include "boxes/sticker_set_box.h" +//#include "boxes/premium_preview_box.h" +#include "lottie/lottie_single_player.h" +//#include "window/window_session_controller.h" +//#include "settings/settings_premium.h" +//#include "apiwrap.h" +#include "styles/style_chat.h" + +namespace HistoryView { +namespace { + +constexpr auto kPremiumToastDuration = 5 * crl::time(1000); + +} // namespace + +PaidReactionToast::PaidReactionToast( + not_null parent, + not_null owner, + rpl::producer topOffset, + Fn view)> mine) +: _parent(parent) +, _owner(owner) +, _topOffset(std::move(topOffset)) { + _owner->viewPaidReactionSent( + ) | rpl::filter( + std::move(mine) + ) | rpl::start_with_next([=](not_null view) { + maybeShowFor(view->data()); + }, _lifetime); +} + +PaidReactionToast::~PaidReactionToast() { + _hiding.push_back(_weak); + for (const auto &weak : base::take(_hiding)) { + if (const auto strong = weak.get()) { + delete strong->widget(); + } + } +} + +void PaidReactionToast::maybeShowFor(not_null item) { + const auto count = item->reactionsPaidScheduled(); + const auto at = _owner->reactions().sendingScheduledPaidAt(item); + if (!count || !at) { + return; + } + const auto left = at - crl::now(); + const auto total = Data::Reactions::ScheduledPaidDelay(); + const auto ignore = total % 1000; + if (left > ignore) { + showFor(item->fullId(), count, left - ignore, total); + } +} + +void PaidReactionToast::showFor( + FullMsgId itemId, + int count, + crl::time left, + crl::time total) { + const auto old = _weak.get(); + const auto i = ranges::find(_stack, itemId); + if (i != end(_stack)) { + if (old && i + 1 == end(_stack)) { + update(old, count, left, total); + return; + } + _stack.erase(i); + } + _stack.push_back(itemId); + + clearHiddenHiding(); + if (old) { + old->hideAnimated(); + _hiding.push_back(_weak); + } + const auto text = tr::lng_paid_react_toast_title( + tr::now, + Ui::Text::Bold + ).append('\n').append(tr::lng_paid_react_toast_text( + tr::now, + lt_count, + count, + Ui::Text::RichLangValue + )); + _st = st::historyPremiumToast; + const auto skip = _st.padding.top(); + const auto size = _st.style.font->height * 2; + const auto undo = tr::lng_paid_react_undo(tr::now); + _st.padding.setLeft(skip + size + skip); + _st.padding.setRight(st::historyPremiumViewSet.font->width(undo) + - st::historyPremiumViewSet.width); + + _weak = Ui::Toast::Show(_parent, Ui::Toast::Config{ + .text = text, + .st = &_st, + .duration = -1, + .multiline = true, + .dark = true, + .slideSide = RectPart::Top, + }); + const auto strong = _weak.get(); + if (!strong) { + return; + } + strong->setInputUsed(true); + const auto widget = strong->widget(); + const auto hideToast = [weak = _weak] { + if (const auto strong = weak.get()) { + strong->hideAnimated(); + } + }; + + const auto button = Ui::CreateChild( + widget.get(), + rpl::single(undo), + st::historyPremiumViewSet); + button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform); + button->show(); + rpl::combine( + widget->sizeValue(), + button->sizeValue() + ) | rpl::start_with_next([=](QSize outer, QSize inner) { + button->moveToRight( + 0, + (outer.height() - inner.height()) / 2, + outer.width()); + }, widget->lifetime()); + const auto preview = Ui::CreateChild(widget.get()); + preview->moveToLeft(skip, skip); + preview->resize(size, size); + preview->show(); + + setupLottiePreview(preview, size); + button->setClickedCallback([=] { + if (const auto item = _owner->message(itemId)) { + _owner->reactions().undoScheduledPaid(item); + } + hideToast(); + }); +} + +void PaidReactionToast::update( + not_null toast, + int count, + crl::time left, + crl::time total) { +} + +void PaidReactionToast::clearHiddenHiding() { + _hiding.erase( + ranges::remove( + _hiding, + nullptr, + &base::weak_ptr::get), + end(_hiding)); +} + +void PaidReactionToast::setupLottiePreview( + not_null widget, + int size) { + const auto generate = [&](const QString &name) { + const auto session = &_owner->session(); + return ChatHelpers::GenerateLocalTgsSticker(session, name); + }; + const auto document = _owner->reactions().paidToastAnimation(); + + const auto bytes = document->createMediaView()->bytes(); + const auto filepath = document->filepath(); + const auto player = widget->lifetime().make_state( + Lottie::ReadContent(bytes, filepath), + Lottie::FrameRequest{ QSize(size, size) }, + Lottie::Quality::Default); + + widget->paintRequest( + ) | rpl::start_with_next([=] { + if (!player->ready()) { + return; + } + const auto image = player->frame(); + QPainter(widget).drawImage( + QRect(QPoint(), image.size() / image.devicePixelRatio()), + image); + if (player->frameIndex() + 1 != player->framesCount()) { + player->markFrameShown(); + } + }, widget->lifetime()); + + player->updates( + ) | rpl::start_with_next([=] { + widget->update(); + }, widget->lifetime()); +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_paid_reaction_toast.h b/Telegram/SourceFiles/history/view/history_view_paid_reaction_toast.h new file mode 100644 index 000000000..5b42ad806 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_paid_reaction_toast.h @@ -0,0 +1,68 @@ +/* +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 "styles/style_widgets.h" + +namespace Data { +class Session; +} // namespace Data + +namespace Ui { +//class Show; +class RpWidget; +} // namespace Ui + +namespace Ui::Toast { +class Instance; +} // namespace Ui::Toast + +namespace HistoryView { + +class Element; + +class PaidReactionToast final { +public: + PaidReactionToast( + not_null parent, + not_null owner, + rpl::producer topOffset, + Fn view)> mine); + ~PaidReactionToast(); + +private: + void maybeShowFor(not_null item); + void showFor( + FullMsgId itemId, + int count, + crl::time left, + crl::time total); + void update( + not_null toast, + int count, + crl::time left, + crl::time total); + + void setupLottiePreview(not_null widget, int size); + void clearHiddenHiding(); + + const not_null _parent; + const not_null _owner; + const rpl::variable _topOffset; + + style::Toast _st; + base::weak_ptr _weak; + std::vector> _hiding; + + std::vector _stack; + + rpl::lifetime _lifetime; + +}; + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/payments/payments_reaction_process.cpp b/Telegram/SourceFiles/payments/payments_reaction_process.cpp index 5e40068b1..0de8a9147 100644 --- a/Telegram/SourceFiles/payments/payments_reaction_process.cpp +++ b/Telegram/SourceFiles/payments/payments_reaction_process.cpp @@ -62,6 +62,8 @@ void TryAddingPaidReaction( if (const auto item = checkItem()) { item->addPaidReaction(count); if (const auto view = weakView.get()) { + const auto history = view->history(); + history->owner().notifyViewPaidReactionSent(view); view->animateReaction({ .id = Data::ReactionId::Paid(), }); diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 1895a8703..dc3751b75 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -472,8 +472,9 @@ FileLoadTask::FileLoadTask( const FileLoadTo &to, const TextWithTags &caption, bool spoiler, - std::shared_ptr album) -: _id(base::RandomValue()) + std::shared_ptr album, + uint64 idOverride) +: _id(idOverride ? idOverride : base::RandomValue()) , _session(session) , _dcId(session->mainDcId()) , _to(to) diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index 2756ac244..d4e99177f 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -224,7 +224,8 @@ public: const FileLoadTo &to, const TextWithTags &caption, bool spoiler, - std::shared_ptr album = nullptr); + std::shared_ptr album = nullptr, + uint64 idOverride = 0); FileLoadTask( not_null session, const QByteArray &voice, diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 95229cd46..40df9722c 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 95229cd46bbba42b431a097705494ec39cce5f0c +Subproject commit 40df9722c97385277f128c21a7fcfc13da52b7c7