mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-03 21:54:05 +02:00
Start paid reaction toast notification.
This commit is contained in:
parent
02610de010
commit
ac92e1c99e
19 changed files with 371 additions and 6 deletions
|
@ -826,6 +826,8 @@ PRIVATE
|
||||||
history/view/history_view_message.cpp
|
history/view/history_view_message.cpp
|
||||||
history/view/history_view_message.h
|
history/view/history_view_message.h
|
||||||
history/view/history_view_object.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.cpp
|
||||||
history/view/history_view_pinned_bar.h
|
history/view/history_view_pinned_bar.h
|
||||||
history/view/history_view_pinned_section.cpp
|
history/view/history_view_pinned_section.cpp
|
||||||
|
|
BIN
Telegram/Resources/animations/star_reaction/toast.tgs
Normal file
BIN
Telegram/Resources/animations/star_reaction/toast.tgs
Normal file
Binary file not shown.
|
@ -3426,6 +3426,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_paid_react_send" = "Send {price}";
|
"lng_paid_react_send" = "Send {price}";
|
||||||
"lng_paid_react_agree" = "By sending stars, you agree to the {link}.";
|
"lng_paid_react_agree" = "By sending stars, you agree to the {link}.";
|
||||||
"lng_paid_react_agree_link" = "Terms of Service";
|
"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_show_original" = "Show Original";
|
||||||
"lng_translate_bar_to" = "Translate to {name}";
|
"lng_translate_bar_to" = "Translate to {name}";
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
<file alias="star_reaction_appear.tgs">../../animations/star_reaction/appear.tgs</file>
|
<file alias="star_reaction_appear.tgs">../../animations/star_reaction/appear.tgs</file>
|
||||||
<file alias="star_reaction_center.tgs">../../animations/star_reaction/center.tgs</file>
|
<file alias="star_reaction_center.tgs">../../animations/star_reaction/center.tgs</file>
|
||||||
<file alias="star_reaction_select.tgs">../../animations/star_reaction/select.tgs</file>
|
<file alias="star_reaction_select.tgs">../../animations/star_reaction/select.tgs</file>
|
||||||
|
<file alias="star_reaction_toast.tgs">../../animations/star_reaction/toast.tgs</file>
|
||||||
<file alias="star_reaction_effect1.tgs">../../animations/star_reaction/effect1.tgs</file>
|
<file alias="star_reaction_effect1.tgs">../../animations/star_reaction/effect1.tgs</file>
|
||||||
<file alias="star_reaction_effect2.tgs">../../animations/star_reaction/effect2.tgs</file>
|
<file alias="star_reaction_effect2.tgs">../../animations/star_reaction/effect2.tgs</file>
|
||||||
<file alias="star_reaction_effect3.tgs">../../animations/star_reaction/effect3.tgs</file>
|
<file alias="star_reaction_effect3.tgs">../../animations/star_reaction/effect3.tgs</file>
|
||||||
|
|
|
@ -24,6 +24,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
|
||||||
|
#include <xxhash.h>
|
||||||
|
|
||||||
namespace ChatHelpers {
|
namespace ChatHelpers {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -315,6 +317,12 @@ QSize ComputeStickerSize(not_null<DocumentData*> document, QSize box) {
|
||||||
return HistoryView::NonEmptySize(request.size(dimensions, 8) / ratio);
|
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<DocumentData*> GenerateLocalTgsSticker(
|
not_null<DocumentData*> GenerateLocalTgsSticker(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
const QString &name) {
|
const QString &name) {
|
||||||
|
@ -327,7 +335,9 @@ not_null<DocumentData*> GenerateLocalTgsSticker(
|
||||||
SendMediaType::File,
|
SendMediaType::File,
|
||||||
FileLoadTo(0, {}, {}, 0),
|
FileLoadTo(0, {}, {}, 0),
|
||||||
{},
|
{},
|
||||||
false);
|
false,
|
||||||
|
nullptr,
|
||||||
|
LocalTgsStickerId(name));
|
||||||
task.process({ .generateGoodThumbnail = false });
|
task.process({ .generateGoodThumbnail = false });
|
||||||
const auto result = task.peekResult();
|
const auto result = task.peekResult();
|
||||||
Assert(result != nullptr);
|
Assert(result != nullptr);
|
||||||
|
|
|
@ -48,7 +48,7 @@ constexpr auto kRecentReactionsLimit = 40;
|
||||||
constexpr auto kMyTagsRequestTimeout = crl::time(1000);
|
constexpr auto kMyTagsRequestTimeout = crl::time(1000);
|
||||||
constexpr auto kTopRequestDelay = 60 * crl::time(1000);
|
constexpr auto kTopRequestDelay = 60 * crl::time(1000);
|
||||||
constexpr auto kTopReactionsLimit = 14;
|
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) {
|
[[nodiscard]] QString ReactionIdToLog(const ReactionId &id) {
|
||||||
if (const auto custom = id.custom()) {
|
if (const auto custom = id.custom()) {
|
||||||
|
@ -1525,6 +1525,15 @@ not_null<Reaction*> Reactions::lookupPaid() {
|
||||||
return &*_paid;
|
return &*_paid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<DocumentData*> Reactions::paidToastAnimation() {
|
||||||
|
if (!_paidToastAnimation) {
|
||||||
|
_paidToastAnimation = ChatHelpers::GenerateLocalTgsSticker(
|
||||||
|
&_owner->session(),
|
||||||
|
u"star_reaction_toast"_q);
|
||||||
|
}
|
||||||
|
return _paidToastAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<std::vector<Reaction>> Reactions::myTagsValue(
|
rpl::producer<std::vector<Reaction>> Reactions::myTagsValue(
|
||||||
SavedSublist *sublist) {
|
SavedSublist *sublist) {
|
||||||
refreshMyTags(sublist);
|
refreshMyTags(sublist);
|
||||||
|
@ -1546,6 +1555,21 @@ void Reactions::schedulePaid(not_null<HistoryItem*> item) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Reactions::undoScheduledPaid(not_null<HistoryItem*> item) {
|
||||||
|
_sendPaidItems.remove(item);
|
||||||
|
item->cancelScheduledPaidReaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
crl::time Reactions::sendingScheduledPaidAt(
|
||||||
|
not_null<HistoryItem*> 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() {
|
void Reactions::repaintCollected() {
|
||||||
const auto now = crl::now();
|
const auto now = crl::now();
|
||||||
auto closest = crl::time();
|
auto closest = crl::time();
|
||||||
|
|
|
@ -139,11 +139,16 @@ public:
|
||||||
void clearTemporary();
|
void clearTemporary();
|
||||||
[[nodiscard]] Reaction *lookupTemporary(const ReactionId &id);
|
[[nodiscard]] Reaction *lookupTemporary(const ReactionId &id);
|
||||||
[[nodiscard]] not_null<Reaction*> lookupPaid();
|
[[nodiscard]] not_null<Reaction*> lookupPaid();
|
||||||
|
[[nodiscard]] not_null<DocumentData*> paidToastAnimation();
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<std::vector<Reaction>> myTagsValue(
|
[[nodiscard]] rpl::producer<std::vector<Reaction>> myTagsValue(
|
||||||
SavedSublist *sublist = nullptr);
|
SavedSublist *sublist = nullptr);
|
||||||
|
|
||||||
void schedulePaid(not_null<HistoryItem*> item);
|
void schedulePaid(not_null<HistoryItem*> item);
|
||||||
|
void undoScheduledPaid(not_null<HistoryItem*> item);
|
||||||
|
[[nodiscard]] crl::time sendingScheduledPaidAt(
|
||||||
|
not_null<HistoryItem*> item) const;
|
||||||
|
[[nodiscard]] static crl::time ScheduledPaidDelay();
|
||||||
|
|
||||||
[[nodiscard]] static bool HasUnread(const MTPMessageReactions &data);
|
[[nodiscard]] static bool HasUnread(const MTPMessageReactions &data);
|
||||||
static void CheckUnknownForUnread(
|
static void CheckUnknownForUnread(
|
||||||
|
@ -293,6 +298,7 @@ private:
|
||||||
// Otherwise we could use flat_map<DocumentId, unique_ptr<Reaction>>.
|
// Otherwise we could use flat_map<DocumentId, unique_ptr<Reaction>>.
|
||||||
std::map<DocumentId, Reaction> _temporary;
|
std::map<DocumentId, Reaction> _temporary;
|
||||||
std::optional<Reaction> _paid;
|
std::optional<Reaction> _paid;
|
||||||
|
DocumentData *_paidToastAnimation = nullptr;
|
||||||
|
|
||||||
base::Timer _topRefreshTimer;
|
base::Timer _topRefreshTimer;
|
||||||
mtpRequestId _topRequestId = 0;
|
mtpRequestId _topRequestId = 0;
|
||||||
|
|
|
@ -1884,6 +1884,14 @@ rpl::producer<not_null<const ViewElement*>> Session::viewRemoved() const {
|
||||||
return _viewRemoved.events();
|
return _viewRemoved.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Session::notifyViewPaidReactionSent(not_null<const ViewElement*> view) {
|
||||||
|
_viewPaidReactionSent.fire_copy(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<not_null<const ViewElement*>> Session::viewPaidReactionSent() const {
|
||||||
|
return _viewPaidReactionSent.events();
|
||||||
|
}
|
||||||
|
|
||||||
void Session::notifyHistoryUnloaded(not_null<const History*> history) {
|
void Session::notifyHistoryUnloaded(not_null<const History*> history) {
|
||||||
_historyUnloaded.fire_copy(history);
|
_historyUnloaded.fire_copy(history);
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,6 +306,8 @@ public:
|
||||||
[[nodiscard]] rpl::producer<not_null<const History*>> historyCleared() const;
|
[[nodiscard]] rpl::producer<not_null<const History*>> historyCleared() const;
|
||||||
void notifyHistoryChangeDelayed(not_null<History*> history);
|
void notifyHistoryChangeDelayed(not_null<History*> history);
|
||||||
[[nodiscard]] rpl::producer<not_null<History*>> historyChanged() const;
|
[[nodiscard]] rpl::producer<not_null<History*>> historyChanged() const;
|
||||||
|
void notifyViewPaidReactionSent(not_null<const ViewElement*> view);
|
||||||
|
[[nodiscard]] rpl::producer<not_null<const ViewElement*>> viewPaidReactionSent() const;
|
||||||
void sendHistoryChangeNotifications();
|
void sendHistoryChangeNotifications();
|
||||||
|
|
||||||
void notifyPinnedDialogsOrderUpdated();
|
void notifyPinnedDialogsOrderUpdated();
|
||||||
|
@ -923,6 +925,7 @@ private:
|
||||||
rpl::event_stream<not_null<HistoryItem*>> _itemDataChanges;
|
rpl::event_stream<not_null<HistoryItem*>> _itemDataChanges;
|
||||||
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
|
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
|
||||||
rpl::event_stream<not_null<const ViewElement*>> _viewRemoved;
|
rpl::event_stream<not_null<const ViewElement*>> _viewRemoved;
|
||||||
|
rpl::event_stream<not_null<const ViewElement*>> _viewPaidReactionSent;
|
||||||
rpl::event_stream<not_null<const History*>> _historyUnloaded;
|
rpl::event_stream<not_null<const History*>> _historyUnloaded;
|
||||||
rpl::event_stream<not_null<const History*>> _historyCleared;
|
rpl::event_stream<not_null<const History*>> _historyCleared;
|
||||||
base::flat_set<not_null<History*>> _historiesChanged;
|
base::flat_set<not_null<History*>> _historiesChanged;
|
||||||
|
|
|
@ -2525,6 +2525,13 @@ void HistoryItem::addPaidReaction(int count) {
|
||||||
_history->owner().notifyItemDataChange(this);
|
_history->owner().notifyItemDataChange(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryItem::cancelScheduledPaidReaction() {
|
||||||
|
if (_reactions) {
|
||||||
|
_reactions->cancelScheduledPaid();
|
||||||
|
_history->owner().notifyItemDataChange(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int HistoryItem::startPaidReactionSending() {
|
int HistoryItem::startPaidReactionSending() {
|
||||||
return _reactions ? _reactions->startPaidSending() : 0;
|
return _reactions ? _reactions->startPaidSending() : 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -446,6 +446,7 @@ public:
|
||||||
const Data::ReactionId &reaction,
|
const Data::ReactionId &reaction,
|
||||||
HistoryReactionSource source);
|
HistoryReactionSource source);
|
||||||
void addPaidReaction(int count);
|
void addPaidReaction(int count);
|
||||||
|
void cancelScheduledPaidReaction();
|
||||||
[[nodiscard]] int startPaidReactionSending();
|
[[nodiscard]] int startPaidReactionSending();
|
||||||
void finishPaidReactionSending(int count, bool success);
|
void finishPaidReactionSending(int count, bool success);
|
||||||
void updateReactionsUnknown();
|
void updateReactionsUnknown();
|
||||||
|
|
|
@ -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_top_bar_widget.h"
|
||||||
#include "history/view/history_view_contact_status.h"
|
#include "history/view/history_view_contact_status.h"
|
||||||
#include "history/view/history_view_context_menu.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_tracker.h"
|
||||||
#include "history/view/history_view_pinned_section.h"
|
#include "history/view/history_view_pinned_section.h"
|
||||||
#include "history/view/history_view_pinned_bar.h"
|
#include "history/view/history_view_pinned_bar.h"
|
||||||
|
@ -285,6 +286,13 @@ HistoryWidget::HistoryWidget(
|
||||||
})
|
})
|
||||||
, _saveDraftTimer([=] { saveDraft(); })
|
, _saveDraftTimer([=] { saveDraft(); })
|
||||||
, _saveCloudDraftTimer([=] { saveCloudDraft(); })
|
, _saveCloudDraftTimer([=] { saveCloudDraft(); })
|
||||||
|
, _paidReactionToast(std::make_unique<HistoryView::PaidReactionToast>(
|
||||||
|
this,
|
||||||
|
&session().data(),
|
||||||
|
rpl::single(st::topBarHeight),
|
||||||
|
[=](not_null<const HistoryView::Element*> view) {
|
||||||
|
return _list && _list->itemTop(view) >= 0;
|
||||||
|
}))
|
||||||
, _topShadow(this) {
|
, _topShadow(this) {
|
||||||
setAcceptDrops(true);
|
setAcceptDrops(true);
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,7 @@ class TabbedSelector;
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
class StickerToast;
|
class StickerToast;
|
||||||
|
class PaidReactionToast;
|
||||||
class TopBarWidget;
|
class TopBarWidget;
|
||||||
class ContactStatus;
|
class ContactStatus;
|
||||||
class BusinessBotStatus;
|
class BusinessBotStatus;
|
||||||
|
@ -825,6 +826,8 @@ private:
|
||||||
std::unique_ptr<HistoryView::StickerToast> _stickerToast;
|
std::unique_ptr<HistoryView::StickerToast> _stickerToast;
|
||||||
std::unique_ptr<ChooseMessagesForReport> _chooseForReport;
|
std::unique_ptr<ChooseMessagesForReport> _chooseForReport;
|
||||||
|
|
||||||
|
std::unique_ptr<HistoryView::PaidReactionToast> _paidReactionToast;
|
||||||
|
|
||||||
base::flat_set<not_null<HistoryItem*>> _itemRevealPending;
|
base::flat_set<not_null<HistoryItem*>> _itemRevealPending;
|
||||||
base::flat_map<
|
base::flat_map<
|
||||||
not_null<HistoryItem*>,
|
not_null<HistoryItem*>,
|
||||||
|
|
|
@ -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<Ui::RpWidget*> parent,
|
||||||
|
not_null<Data::Session*> owner,
|
||||||
|
rpl::producer<int> topOffset,
|
||||||
|
Fn<bool(not_null<const Element*> view)> mine)
|
||||||
|
: _parent(parent)
|
||||||
|
, _owner(owner)
|
||||||
|
, _topOffset(std::move(topOffset)) {
|
||||||
|
_owner->viewPaidReactionSent(
|
||||||
|
) | rpl::filter(
|
||||||
|
std::move(mine)
|
||||||
|
) | rpl::start_with_next([=](not_null<const Element*> 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<HistoryItem*> 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<Ui::RoundButton>(
|
||||||
|
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<Ui::RpWidget>(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<Ui::Toast::Instance*> toast,
|
||||||
|
int count,
|
||||||
|
crl::time left,
|
||||||
|
crl::time total) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void PaidReactionToast::clearHiddenHiding() {
|
||||||
|
_hiding.erase(
|
||||||
|
ranges::remove(
|
||||||
|
_hiding,
|
||||||
|
nullptr,
|
||||||
|
&base::weak_ptr<Ui::Toast::Instance>::get),
|
||||||
|
end(_hiding));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PaidReactionToast::setupLottiePreview(
|
||||||
|
not_null<Ui::RpWidget*> 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::SinglePlayer>(
|
||||||
|
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
|
|
@ -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<Ui::RpWidget*> parent,
|
||||||
|
not_null<Data::Session*> owner,
|
||||||
|
rpl::producer<int> topOffset,
|
||||||
|
Fn<bool(not_null<const Element*> view)> mine);
|
||||||
|
~PaidReactionToast();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void maybeShowFor(not_null<HistoryItem*> item);
|
||||||
|
void showFor(
|
||||||
|
FullMsgId itemId,
|
||||||
|
int count,
|
||||||
|
crl::time left,
|
||||||
|
crl::time total);
|
||||||
|
void update(
|
||||||
|
not_null<Ui::Toast::Instance*> toast,
|
||||||
|
int count,
|
||||||
|
crl::time left,
|
||||||
|
crl::time total);
|
||||||
|
|
||||||
|
void setupLottiePreview(not_null<Ui::RpWidget*> widget, int size);
|
||||||
|
void clearHiddenHiding();
|
||||||
|
|
||||||
|
const not_null<Ui::RpWidget*> _parent;
|
||||||
|
const not_null<Data::Session*> _owner;
|
||||||
|
const rpl::variable<int> _topOffset;
|
||||||
|
|
||||||
|
style::Toast _st;
|
||||||
|
base::weak_ptr<Ui::Toast::Instance> _weak;
|
||||||
|
std::vector<base::weak_ptr<Ui::Toast::Instance>> _hiding;
|
||||||
|
|
||||||
|
std::vector<FullMsgId> _stack;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
|
@ -62,6 +62,8 @@ void TryAddingPaidReaction(
|
||||||
if (const auto item = checkItem()) {
|
if (const auto item = checkItem()) {
|
||||||
item->addPaidReaction(count);
|
item->addPaidReaction(count);
|
||||||
if (const auto view = weakView.get()) {
|
if (const auto view = weakView.get()) {
|
||||||
|
const auto history = view->history();
|
||||||
|
history->owner().notifyViewPaidReactionSent(view);
|
||||||
view->animateReaction({
|
view->animateReaction({
|
||||||
.id = Data::ReactionId::Paid(),
|
.id = Data::ReactionId::Paid(),
|
||||||
});
|
});
|
||||||
|
|
|
@ -472,8 +472,9 @@ FileLoadTask::FileLoadTask(
|
||||||
const FileLoadTo &to,
|
const FileLoadTo &to,
|
||||||
const TextWithTags &caption,
|
const TextWithTags &caption,
|
||||||
bool spoiler,
|
bool spoiler,
|
||||||
std::shared_ptr<SendingAlbum> album)
|
std::shared_ptr<SendingAlbum> album,
|
||||||
: _id(base::RandomValue<uint64>())
|
uint64 idOverride)
|
||||||
|
: _id(idOverride ? idOverride : base::RandomValue<uint64>())
|
||||||
, _session(session)
|
, _session(session)
|
||||||
, _dcId(session->mainDcId())
|
, _dcId(session->mainDcId())
|
||||||
, _to(to)
|
, _to(to)
|
||||||
|
|
|
@ -224,7 +224,8 @@ public:
|
||||||
const FileLoadTo &to,
|
const FileLoadTo &to,
|
||||||
const TextWithTags &caption,
|
const TextWithTags &caption,
|
||||||
bool spoiler,
|
bool spoiler,
|
||||||
std::shared_ptr<SendingAlbum> album = nullptr);
|
std::shared_ptr<SendingAlbum> album = nullptr,
|
||||||
|
uint64 idOverride = 0);
|
||||||
FileLoadTask(
|
FileLoadTask(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
const QByteArray &voice,
|
const QByteArray &voice,
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 95229cd46bbba42b431a097705494ec39cce5f0c
|
Subproject commit 40df9722c97385277f128c21a7fcfc13da52b7c7
|
Loading…
Add table
Reference in a new issue