mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 05:07:10 +02:00
Allow enabling paid reactions.
This commit is contained in:
parent
126fd89bb7
commit
bb3fc17489
33 changed files with 248 additions and 80 deletions
BIN
Telegram/Resources/animations/star_reaction/appear.tgs
Normal file
BIN
Telegram/Resources/animations/star_reaction/appear.tgs
Normal file
Binary file not shown.
BIN
Telegram/Resources/animations/star_reaction/effect.tgs
Normal file
BIN
Telegram/Resources/animations/star_reaction/effect.tgs
Normal file
Binary file not shown.
BIN
Telegram/Resources/animations/star_reaction/select.tgs
Normal file
BIN
Telegram/Resources/animations/star_reaction/select.tgs
Normal file
Binary file not shown.
|
@ -1550,6 +1550,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_manage_peer_reactions_max_slider#other" = "{count} reactions per post";
|
||||
"lng_manage_peer_reactions_max_about" = "Limit the number of different reactions that can be added to a post, including already published ones.";
|
||||
|
||||
"lng_manage_peer_reactions_paid" = "Enable Paid Reactions";
|
||||
"lng_manage_peer_reactions_paid_about" = "Switch this on to let your subscribers set paid reactions with Telegram Stars, which you will be able to withdraw later as TON. {link}";
|
||||
"lng_manage_peer_reactions_paid_link" = "Learn more >";
|
||||
|
||||
"lng_manage_peer_antispam" = "Aggressive Anti-Spam";
|
||||
"lng_manage_peer_antispam_about" = "Telegram will filter more spam but may occasionally affect ordinary messages. You can report False Positives in Recent Actions.";
|
||||
"lng_manage_peer_antispam_not_enough#one" = "Aggressive filtering can be enabled only in groups with more than **{count} member**.";
|
||||
|
|
|
@ -28,5 +28,20 @@
|
|||
<file alias="collectible_phone.tgs">../../animations/collectible_phone.tgs</file>
|
||||
<file alias="search.tgs">../../animations/search.tgs</file>
|
||||
<file alias="noresults.tgs">../../animations/noresults.tgs</file>
|
||||
|
||||
<file alias="dice_idle.tgs">../../animations/dice/dice_idle.tgs</file>
|
||||
<file alias="dart_idle.tgs">../../animations/dice/dart_idle.tgs</file>
|
||||
<file alias="bball_idle.tgs">../../animations/dice/bball_idle.tgs</file>
|
||||
<file alias="fball_idle.tgs">../../animations/dice/fball_idle.tgs</file>
|
||||
<file alias="slot_0_idle.tgs">../../animations/dice/slot_0_idle.tgs</file>
|
||||
<file alias="slot_1_idle.tgs">../../animations/dice/slot_1_idle.tgs</file>
|
||||
<file alias="slot_2_idle.tgs">../../animations/dice/slot_2_idle.tgs</file>
|
||||
<file alias="slot_back.tgs">../../animations/dice/slot_back.tgs</file>
|
||||
<file alias="slot_pull.tgs">../../animations/dice/slot_pull.tgs</file>
|
||||
<file alias="winners.tgs">../../animations/dice/winners.tgs</file>
|
||||
|
||||
<file alias="star_reaction_appear.tgs">../../animations/star_reaction/appear.tgs</file>
|
||||
<file alias="star_reaction_effect.tgs">../../animations/star_reaction/effect.tgs</file>
|
||||
<file alias="star_reaction_select.tgs">../../animations/star_reaction/select.tgs</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -7,16 +7,6 @@
|
|||
<file alias="art/logo_256.png">../../art/logo_256.png</file>
|
||||
<file alias="art/logo_256_no_margin.png">../../art/logo_256_no_margin.png</file>
|
||||
<file alias="art/themeimage.jpg">../../art/themeimage.jpg</file>
|
||||
<file alias="art/dice_idle.tgs">../../art/dice_idle.tgs</file>
|
||||
<file alias="art/dart_idle.tgs">../../art/dart_idle.tgs</file>
|
||||
<file alias="art/bball_idle.tgs">../../art/bball_idle.tgs</file>
|
||||
<file alias="art/fball_idle.tgs">../../art/fball_idle.tgs</file>
|
||||
<file alias="art/slot_0_idle.tgs">../../art/slot_0_idle.tgs</file>
|
||||
<file alias="art/slot_1_idle.tgs">../../art/slot_1_idle.tgs</file>
|
||||
<file alias="art/slot_2_idle.tgs">../../art/slot_2_idle.tgs</file>
|
||||
<file alias="art/slot_back.tgs">../../art/slot_back.tgs</file>
|
||||
<file alias="art/slot_pull.tgs">../../art/slot_pull.tgs</file>
|
||||
<file alias="art/winners.tgs">../../art/winners.tgs</file>
|
||||
<file alias="day-blue.tdesktop-theme">../../day-blue.tdesktop-theme</file>
|
||||
<file alias="night.tdesktop-theme">../../night.tdesktop-theme</file>
|
||||
<file alias="night-green.tdesktop-theme">../../night-green.tdesktop-theme</file>
|
||||
|
|
|
@ -608,6 +608,7 @@ void EditAllowedReactionsBox(
|
|||
rpl::variable<SelectorState> selectorState;
|
||||
std::vector<Data::ReactionId> selected;
|
||||
rpl::variable<int> customCount;
|
||||
bool paidEnabled = false;
|
||||
};
|
||||
const auto allowed = args.allowed;
|
||||
const auto optionInitial = (allowed.type != AllowedReactionsType::Some)
|
||||
|
@ -617,6 +618,7 @@ void EditAllowedReactionsBox(
|
|||
: Option::Some;
|
||||
const auto state = box->lifetime().make_state<State>(State{
|
||||
.option = optionInitial,
|
||||
.paidEnabled = allowed.paidEnabled,
|
||||
});
|
||||
|
||||
const auto container = box->verticalLayout();
|
||||
|
@ -847,6 +849,29 @@ void EditAllowedReactionsBox(
|
|||
) | rpl::map(rpl::mappers::_1 == SelectorState::Active)));
|
||||
|
||||
Ui::AddDividerText(inner, tr::lng_manage_peer_reactions_max_about());
|
||||
|
||||
Ui::AddSkip(inner);
|
||||
const auto paid = inner->add(object_ptr<Ui::SettingsButton>(
|
||||
inner,
|
||||
tr::lng_manage_peer_reactions_paid(),
|
||||
st::manageGroupNoIconButton.button));
|
||||
paid->toggleOn(rpl::single(allowed.paidEnabled));
|
||||
paid->toggledValue(
|
||||
) | rpl::start_with_next([=](bool value) {
|
||||
state->paidEnabled = value;
|
||||
}, paid->lifetime());
|
||||
Ui::AddSkip(inner);
|
||||
|
||||
Ui::AddDividerText(
|
||||
inner,
|
||||
tr::lng_manage_peer_reactions_paid_about(
|
||||
lt_link,
|
||||
tr::lng_manage_peer_reactions_paid_link([=](QString text) {
|
||||
return Ui::Text::Link(
|
||||
text,
|
||||
u"https://telegram.org/tos/stars"_q);
|
||||
}),
|
||||
Ui::Text::WithEntities));
|
||||
}
|
||||
const auto collect = [=] {
|
||||
auto result = AllowedReactions();
|
||||
|
@ -856,6 +881,9 @@ void EditAllowedReactionsBox(
|
|||
: (enabled->toggled())) {
|
||||
result.some = state->selected;
|
||||
}
|
||||
if (!isGroup && enabled->toggled()) {
|
||||
result.paidEnabled = state->paidEnabled;
|
||||
}
|
||||
auto some = result.some;
|
||||
auto simple = all | ranges::views::transform(
|
||||
&Data::Reaction::id
|
||||
|
@ -907,16 +935,20 @@ void SaveAllowedReactions(
|
|||
: allowed.some.empty()
|
||||
? MTP_chatReactionsNone()
|
||||
: MTP_chatReactionsSome(MTP_vector<MTPReaction>(ids));
|
||||
const auto editPaidEnabled = peer->isBroadcast();
|
||||
const auto paidEnabled = editPaidEnabled && allowed.paidEnabled;
|
||||
const auto maxCount = allowed.maxCount;
|
||||
peer->session().api().request(MTPmessages_SetChatAvailableReactions(
|
||||
allowed.maxCount ? MTP_flags(Flag::f_reactions_limit) : MTP_flags(0),
|
||||
MTP_flags(Flag()
|
||||
| (maxCount ? Flag::f_reactions_limit : Flag())
|
||||
| (editPaidEnabled ? Flag::f_paid_enabled : Flag())),
|
||||
peer->input,
|
||||
updated,
|
||||
MTP_int(allowed.maxCount),
|
||||
MTPbool() // paid_enabled
|
||||
MTP_int(maxCount),
|
||||
MTP_bool(paidEnabled)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
peer->session().api().applyUpdates(result);
|
||||
auto parsed = Data::Parse(updated);
|
||||
parsed.maxCount = allowed.maxCount;
|
||||
auto parsed = Data::Parse(updated, maxCount, paidEnabled);
|
||||
if (const auto chat = peer->asChat()) {
|
||||
chat->setAllowedReactions(parsed);
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
|
|
|
@ -8,11 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "chat_helpers/stickers_dice_pack.h"
|
||||
|
||||
#include "main/main_session.h"
|
||||
#include "chat_helpers/stickers_lottie.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_document.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
#include "ui/image/image_location_factory.h"
|
||||
#include "storage/localimageloader.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
|
@ -104,6 +102,11 @@ void DicePack::tryGenerateLocalZero() {
|
|||
return;
|
||||
}
|
||||
|
||||
const auto generateLocal = [&](int index, const QString &name) {
|
||||
_map.emplace(
|
||||
index,
|
||||
ChatHelpers::GenerateLocalTgsSticker(_session, name));
|
||||
};
|
||||
if (_emoji == DicePacks::kDiceString) {
|
||||
generateLocal(0, u"dice_idle"_q);
|
||||
} else if (_emoji == DicePacks::kDartString) {
|
||||
|
@ -123,32 +126,8 @@ void DicePack::tryGenerateLocalZero() {
|
|||
}
|
||||
}
|
||||
|
||||
void DicePack::generateLocal(int index, const QString &name) {
|
||||
const auto path = u":/gui/art/"_q + name + u".tgs"_q;
|
||||
auto task = FileLoadTask(
|
||||
_session,
|
||||
path,
|
||||
QByteArray(),
|
||||
nullptr,
|
||||
SendMediaType::File,
|
||||
FileLoadTo(0, {}, {}, 0),
|
||||
{},
|
||||
false);
|
||||
task.process({ .generateGoodThumbnail = false });
|
||||
const auto result = task.peekResult();
|
||||
Assert(result != nullptr);
|
||||
const auto document = _session->data().processDocument(
|
||||
result->document,
|
||||
Images::FromImageInMemory(result->thumb, "WEBP", result->thumbbytes));
|
||||
document->setLocation(Core::FileLocation(path));
|
||||
|
||||
_map.emplace(index, document);
|
||||
|
||||
Ensures(document->sticker());
|
||||
Ensures(document->sticker()->isLottie());
|
||||
}
|
||||
|
||||
DicePacks::DicePacks(not_null<Main::Session*> session) : _session(session) {
|
||||
DicePacks::DicePacks(not_null<Main::Session*> session)
|
||||
: _session(session) {
|
||||
}
|
||||
|
||||
DocumentData *DicePacks::lookup(const QString &emoji, int value) {
|
||||
|
|
|
@ -26,7 +26,6 @@ private:
|
|||
void load();
|
||||
void applySet(const MTPDmessages_stickerSet &data);
|
||||
void tryGenerateLocalZero();
|
||||
void generateLocal(int index, const QString &name);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
QString _emoji;
|
||||
|
|
|
@ -15,9 +15,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_session.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "storage/cache/storage_cache_database.h"
|
||||
#include "storage/localimageloader.h"
|
||||
#include "history/view/media/history_view_media_common.h"
|
||||
#include "media/clip/media_clip_reader.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
#include "ui/effects/path_shift_gradient.h"
|
||||
#include "ui/image/image_location_factory.h"
|
||||
#include "ui/painter.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
|
@ -312,4 +315,33 @@ QSize ComputeStickerSize(not_null<DocumentData*> document, QSize box) {
|
|||
return HistoryView::NonEmptySize(request.size(dimensions, 8) / ratio);
|
||||
}
|
||||
|
||||
not_null<DocumentData*> GenerateLocalTgsSticker(
|
||||
not_null<Main::Session*> session,
|
||||
const QString &name) {
|
||||
const auto path = u":/animations/"_q + name + u".tgs"_q;
|
||||
auto task = FileLoadTask(
|
||||
session,
|
||||
path,
|
||||
QByteArray(),
|
||||
nullptr,
|
||||
SendMediaType::File,
|
||||
FileLoadTo(0, {}, {}, 0),
|
||||
{},
|
||||
false);
|
||||
task.process({ .generateGoodThumbnail = false });
|
||||
const auto result = task.peekResult();
|
||||
Assert(result != nullptr);
|
||||
const auto document = session->data().processDocument(
|
||||
result->document,
|
||||
Images::FromImageInMemory(
|
||||
result->thumb,
|
||||
"WEBP",
|
||||
result->thumbbytes));
|
||||
document->setLocation(Core::FileLocation(path));
|
||||
|
||||
Ensures(document->sticker());
|
||||
Ensures(document->sticker()->isLottie());
|
||||
return document;
|
||||
}
|
||||
|
||||
} // namespace ChatHelpers
|
||||
|
|
|
@ -130,4 +130,8 @@ bool PaintStickerThumbnailPath(
|
|||
not_null<DocumentData*> document,
|
||||
QSize box);
|
||||
|
||||
[[nodiscard]] not_null<DocumentData*> GenerateLocalTgsSticker(
|
||||
not_null<Main::Session*> session,
|
||||
const QString &name);
|
||||
|
||||
} // namespace ChatHelpers
|
||||
|
|
|
@ -1220,11 +1220,16 @@ void ApplyChannelUpdate(
|
|||
|
||||
const auto reactionsLimit = update.vreactions_limit().value_or_empty();
|
||||
if (const auto allowed = update.vavailable_reactions()) {
|
||||
auto parsed = Data::Parse(*allowed);
|
||||
parsed.maxCount = reactionsLimit;
|
||||
auto parsed = Data::Parse(
|
||||
*allowed,
|
||||
reactionsLimit,
|
||||
update.is_paid_reactions_available());
|
||||
channel->setAllowedReactions(std::move(parsed));
|
||||
} else {
|
||||
channel->setAllowedReactions({ .maxCount = reactionsLimit });
|
||||
channel->setAllowedReactions({
|
||||
.maxCount = reactionsLimit,
|
||||
.paidEnabled = update.is_paid_reactions_available(),
|
||||
});
|
||||
}
|
||||
channel->owner().stories().apply(channel, update.vstories());
|
||||
channel->fullUpdated();
|
||||
|
|
|
@ -486,8 +486,8 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
|
|||
chat->setTranslationDisabled(update.is_translations_disabled());
|
||||
const auto reactionsLimit = update.vreactions_limit().value_or_empty();
|
||||
if (const auto allowed = update.vavailable_reactions()) {
|
||||
auto parsed = Data::Parse(*allowed);
|
||||
parsed.maxCount = reactionsLimit;
|
||||
const auto paidEnabled = false;
|
||||
auto parsed = Data::Parse(*allowed, reactionsLimit, paidEnabled);
|
||||
chat->setAllowedReactions(std::move(parsed));
|
||||
} else {
|
||||
chat->setAllowedReactions({ .maxCount = reactionsLimit });
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "data/data_message_reactions.h"
|
||||
|
||||
#include "chat_helpers/stickers_lottie.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_components.h"
|
||||
|
@ -29,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "mtproto/mtproto_config.h"
|
||||
#include "base/timer_rpl.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
|
@ -204,9 +206,13 @@ PossibleItemReactionsRef LookupPossibleReactions(
|
|||
}
|
||||
} else {
|
||||
const auto &allowed = PeerAllowedReactions(peer);
|
||||
result.recent.reserve((allowed.type == AllowedReactionsType::Some)
|
||||
? allowed.some.size()
|
||||
: full.size());
|
||||
result.recent.reserve((allowed.paidEnabled ? 1 : 0)
|
||||
+ ((allowed.type == AllowedReactionsType::Some)
|
||||
? allowed.some.size()
|
||||
: full.size()));
|
||||
if (allowed.paidEnabled) {
|
||||
result.recent.push_back(reactions->lookupPaid());
|
||||
}
|
||||
add([&](const Reaction &reaction) {
|
||||
const auto id = reaction.id;
|
||||
if (id.custom() && !premiumPossible) {
|
||||
|
@ -569,7 +575,7 @@ rpl::producer<> Reactions::effectsUpdates() const {
|
|||
}
|
||||
|
||||
void Reactions::preloadReactionImageFor(const ReactionId &emoji) {
|
||||
if (!emoji.emoji().isEmpty()) {
|
||||
if (emoji.paid() || !emoji.emoji().isEmpty()) {
|
||||
preloadImageFor(emoji);
|
||||
}
|
||||
}
|
||||
|
@ -586,6 +592,10 @@ void Reactions::preloadImageFor(const ReactionId &id) {
|
|||
}
|
||||
auto &set = _images.emplace(id).first->second;
|
||||
set.effect = (id.custom() != 0);
|
||||
if (id.paid()) {
|
||||
loadImage(set, lookupPaid()->selectAnimation, true);
|
||||
return;
|
||||
}
|
||||
auto &list = set.effect ? _effects : _available;
|
||||
const auto i = ranges::find(list, id, &Reaction::id);
|
||||
const auto document = (i == end(list))
|
||||
|
@ -1356,7 +1366,10 @@ void Reactions::send(not_null<HistoryItem*> item, bool addToRecent) {
|
|||
MTP_flags(flags),
|
||||
item->history()->peer->input,
|
||||
MTP_int(id.msg),
|
||||
MTP_vector<MTPReaction>(chosen | ranges::views::transform(
|
||||
MTP_vector<MTPReaction>(chosen | ranges::views::filter([](
|
||||
const ReactionId &id) {
|
||||
return !id.paid();
|
||||
}) | ranges::views::transform(
|
||||
ReactionToMTP
|
||||
) | ranges::to<QVector<MTPReaction>>())
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
|
@ -1367,6 +1380,21 @@ void Reactions::send(not_null<HistoryItem*> item, bool addToRecent) {
|
|||
}).send();
|
||||
}
|
||||
|
||||
void Reactions::sendPaid(not_null<HistoryItem*> item, int count) {
|
||||
const auto id = item->fullId();
|
||||
const auto randomId = base::unixtime::mtproto_msg_id();
|
||||
auto &api = _owner->session().api();
|
||||
api.request(MTPmessages_SendPaidReaction(
|
||||
item->history()->peer->input,
|
||||
MTP_int(id.msg),
|
||||
MTP_int(count),
|
||||
MTP_long(randomId)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_owner->session().api().applyUpdates(result);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
}).send();
|
||||
}
|
||||
|
||||
void Reactions::poll(not_null<HistoryItem*> item, crl::time now) {
|
||||
// Group them by one second.
|
||||
const auto last = item->lastReactionsRefreshTime();
|
||||
|
@ -1403,7 +1431,9 @@ void Reactions::clearTemporary() {
|
|||
}
|
||||
|
||||
Reaction *Reactions::lookupTemporary(const ReactionId &id) {
|
||||
if (const auto emoji = id.emoji(); !emoji.isEmpty()) {
|
||||
if (id.paid()) {
|
||||
return lookupPaid();
|
||||
} else if (const auto emoji = id.emoji(); !emoji.isEmpty()) {
|
||||
const auto i = ranges::find(_available, id, &Reaction::id);
|
||||
return (i != end(_available)) ? &*i : nullptr;
|
||||
} else if (const auto customId = id.custom()) {
|
||||
|
@ -1424,6 +1454,26 @@ Reaction *Reactions::lookupTemporary(const ReactionId &id) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
not_null<Reaction*> Reactions::lookupPaid() {
|
||||
if (!_paid) {
|
||||
const auto generate = [&](const QString &name) {
|
||||
const auto session = &_owner->session();
|
||||
return ChatHelpers::GenerateLocalTgsSticker(session, name);
|
||||
};
|
||||
const auto select = generate(u"star_reaction_select"_q);
|
||||
_paid.emplace(Reaction{
|
||||
.id = ReactionId::Paid(),
|
||||
.title = u"Telegram Star"_q,
|
||||
.appearAnimation = generate(u"star_reaction_appear"_q),
|
||||
.selectAnimation = select,
|
||||
.centerIcon = select,
|
||||
//.aroundAnimation = generate(u"star_reaction_effect"_q),
|
||||
.active = true,
|
||||
});
|
||||
}
|
||||
return &*_paid;
|
||||
}
|
||||
|
||||
rpl::producer<std::vector<Reaction>> Reactions::myTagsValue(
|
||||
SavedSublist *sublist) {
|
||||
refreshMyTags(sublist);
|
||||
|
@ -1529,6 +1579,25 @@ MessageReactions::MessageReactions(not_null<HistoryItem*> item)
|
|||
: _item(item) {
|
||||
}
|
||||
|
||||
void MessageReactions::addPaid(int count) {
|
||||
Expects(_item->history()->peer->isBroadcast());
|
||||
|
||||
const auto id = Data::ReactionId::Paid();
|
||||
const auto history = _item->history();
|
||||
const auto peer = history->peer;
|
||||
const auto i = ranges::find(_list, id, &MessageReaction::id);
|
||||
if (i != end(_list)) {
|
||||
i->my = true;
|
||||
i->count += count;
|
||||
std::rotate(i, i + 1, end(_list));
|
||||
} else {
|
||||
_list.push_back({ .id = id, .count = count, .my = true });
|
||||
}
|
||||
auto &owner = history->owner();
|
||||
owner.reactions().sendPaid(_item, count);
|
||||
owner.notifyItemDataChange(_item);
|
||||
}
|
||||
|
||||
void MessageReactions::add(const ReactionId &id, bool addToRecent) {
|
||||
Expects(!id.empty());
|
||||
|
||||
|
|
|
@ -129,6 +129,7 @@ public:
|
|||
void preloadAnimationsFor(const ReactionId &emoji);
|
||||
|
||||
void send(not_null<HistoryItem*> item, bool addToRecent);
|
||||
void sendPaid(not_null<HistoryItem*> item, int count);
|
||||
[[nodiscard]] bool sending(not_null<HistoryItem*> item) const;
|
||||
|
||||
void poll(not_null<HistoryItem*> item, crl::time now);
|
||||
|
@ -137,6 +138,7 @@ public:
|
|||
|
||||
void clearTemporary();
|
||||
[[nodiscard]] Reaction *lookupTemporary(const ReactionId &id);
|
||||
[[nodiscard]] not_null<Reaction*> lookupPaid();
|
||||
|
||||
[[nodiscard]] rpl::producer<std::vector<Reaction>> myTagsValue(
|
||||
SavedSublist *sublist = nullptr);
|
||||
|
@ -275,6 +277,7 @@ private:
|
|||
// So we use std::map instead of base::flat_map here.
|
||||
// Otherwise we could use flat_map<DocumentId, unique_ptr<Reaction>>.
|
||||
std::map<DocumentId, Reaction> _temporary;
|
||||
std::optional<Reaction> _paid;
|
||||
|
||||
base::Timer _topRefreshTimer;
|
||||
mtpRequestId _topRequestId = 0;
|
||||
|
@ -333,6 +336,7 @@ public:
|
|||
explicit MessageReactions(not_null<HistoryItem*> item);
|
||||
|
||||
void add(const ReactionId &id, bool addToRecent);
|
||||
void addPaid(int count);
|
||||
void remove(const ReactionId &id);
|
||||
bool change(
|
||||
const QVector<MTPReactionCount> &list,
|
||||
|
|
|
@ -95,28 +95,22 @@ bool ApplyBotMenuButton(
|
|||
return changed;
|
||||
}
|
||||
|
||||
bool operator<(
|
||||
const AllowedReactions &a,
|
||||
const AllowedReactions &b) {
|
||||
return (a.type < b.type) || ((a.type == b.type) && (a.some < b.some));
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
const AllowedReactions &a,
|
||||
const AllowedReactions &b) {
|
||||
return (a.type == b.type)
|
||||
&& (a.some == b.some)
|
||||
&& (a.maxCount == b.maxCount);
|
||||
}
|
||||
|
||||
AllowedReactions Parse(const MTPChatReactions &value) {
|
||||
AllowedReactions Parse(
|
||||
const MTPChatReactions &value,
|
||||
int maxCount,
|
||||
bool paidEnabled) {
|
||||
return value.match([&](const MTPDchatReactionsNone &) {
|
||||
return AllowedReactions();
|
||||
return AllowedReactions{
|
||||
.maxCount = maxCount,
|
||||
.paidEnabled = paidEnabled,
|
||||
};
|
||||
}, [&](const MTPDchatReactionsAll &data) {
|
||||
return AllowedReactions{
|
||||
.maxCount = maxCount,
|
||||
.type = (data.is_allow_custom()
|
||||
? AllowedReactionsType::All
|
||||
: AllowedReactionsType::Default),
|
||||
.paidEnabled = paidEnabled,
|
||||
};
|
||||
}, [&](const MTPDchatReactionsSome &data) {
|
||||
return AllowedReactions{
|
||||
|
@ -125,7 +119,9 @@ AllowedReactions Parse(const MTPChatReactions &value) {
|
|||
) | ranges::views::transform(
|
||||
ReactionFromMTP
|
||||
) | ranges::to_vector,
|
||||
.maxCount = maxCount,
|
||||
.type = AllowedReactionsType::Some,
|
||||
.paidEnabled = paidEnabled,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ bool ApplyBotMenuButton(
|
|||
not_null<BotInfo*> info,
|
||||
const MTPBotMenuButton *button);
|
||||
|
||||
enum class AllowedReactionsType {
|
||||
enum class AllowedReactionsType : uchar {
|
||||
All,
|
||||
Default,
|
||||
Some,
|
||||
|
@ -109,14 +109,19 @@ enum class AllowedReactionsType {
|
|||
|
||||
struct AllowedReactions {
|
||||
std::vector<ReactionId> some;
|
||||
AllowedReactionsType type = AllowedReactionsType::Some;
|
||||
int maxCount = 0;
|
||||
AllowedReactionsType type = AllowedReactionsType::Some;
|
||||
bool paidEnabled = false;
|
||||
|
||||
friend inline bool operator==(
|
||||
const AllowedReactions &,
|
||||
const AllowedReactions &) = default;
|
||||
};
|
||||
|
||||
bool operator<(const AllowedReactions &a, const AllowedReactions &b);
|
||||
bool operator==(const AllowedReactions &a, const AllowedReactions &b);
|
||||
|
||||
[[nodiscard]] AllowedReactions Parse(const MTPChatReactions &value);
|
||||
[[nodiscard]] AllowedReactions Parse(
|
||||
const MTPChatReactions &value,
|
||||
int maxCount,
|
||||
bool paidEnabled);
|
||||
[[nodiscard]] PeerData *PeerFromInputMTP(
|
||||
not_null<Session*> owner,
|
||||
const MTPInputPeer &input);
|
||||
|
|
|
@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/effects/reaction_fly_animation.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/text/text_isolated_emoji.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/boxes/edit_factcheck_box.h"
|
||||
#include "ui/boxes/report_box.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
|
@ -488,6 +489,15 @@ void HistoryInner::reactionChosen(const ChosenReaction &reaction) {
|
|||
const auto item = session().data().message(reaction.context);
|
||||
if (!item) {
|
||||
return;
|
||||
} else if (reaction.id.paid()) {
|
||||
_controller->show(Ui::MakeConfirmBox({
|
||||
.text = u"Send 10-stars reaction?"_q,
|
||||
.confirmed = [=](Fn<void()> close) {
|
||||
item->addPaidReaction(10, HistoryItem::ReactionSource::Selector);
|
||||
close();
|
||||
},
|
||||
}));
|
||||
return;
|
||||
} else if (Window::ShowReactPremiumError(
|
||||
_controller,
|
||||
item,
|
||||
|
|
|
@ -2514,6 +2514,16 @@ bool HistoryItem::canReact() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
void HistoryItem::addPaidReaction(int count, ReactionSource source) {
|
||||
Expects(_history->peer->isBroadcast());
|
||||
|
||||
if (!_reactions) {
|
||||
_reactions = std::make_unique<Data::MessageReactions>(this);
|
||||
}
|
||||
_reactions->addPaid(count);
|
||||
_history->owner().notifyItemDataChange(this);
|
||||
}
|
||||
|
||||
void HistoryItem::toggleReaction(
|
||||
const Data::ReactionId &reaction,
|
||||
ReactionSource source) {
|
||||
|
@ -2530,7 +2540,6 @@ void HistoryItem::toggleReaction(
|
|||
if (_reactions->empty()) {
|
||||
_reactions = nullptr;
|
||||
_flags &= ~MessageFlag::CanViewReactions;
|
||||
_history->owner().notifyItemDataChange(this);
|
||||
}
|
||||
} else {
|
||||
_reactions->add(reaction, (source == ReactionSource::Selector));
|
||||
|
|
|
@ -443,6 +443,7 @@ public:
|
|||
void toggleReaction(
|
||||
const Data::ReactionId &reaction,
|
||||
ReactionSource source);
|
||||
void addPaidReaction(int count, ReactionSource source);
|
||||
void updateReactionsUnknown();
|
||||
[[nodiscard]] auto reactions() const
|
||||
-> const std::vector<Data::MessageReaction> &;
|
||||
|
|
|
@ -3306,7 +3306,12 @@ void Message::refreshReactions() {
|
|||
ClickContext context) {
|
||||
if (const auto strong = weak.get()) {
|
||||
const auto item = strong->data();
|
||||
if (item->reactionsAreTags()) {
|
||||
if (id.paid()) {
|
||||
item->addPaidReaction(
|
||||
1,
|
||||
HistoryItem::ReactionSource::Existing);
|
||||
return;
|
||||
} else if (item->reactionsAreTags()) {
|
||||
if (item->history()->session().premium()) {
|
||||
const auto tag = Data::SearchTagToQuery(id);
|
||||
HashtagClickHandler(tag).onClick(context);
|
||||
|
|
|
@ -144,6 +144,10 @@ void InlineList::layoutButtons() {
|
|||
return true;
|
||||
} else if (acount < bcount) {
|
||||
return false;
|
||||
} else if (b->id.paid()) {
|
||||
return false;
|
||||
} else if (a->id.paid()) {
|
||||
return true;
|
||||
}
|
||||
return ranges::find(list, a->id, &::Data::Reaction::id)
|
||||
< ranges::find(list, b->id, &::Data::Reaction::id);
|
||||
|
|
|
@ -86,6 +86,11 @@ ReactionFlyAnimation::ReactionFlyAnimation(
|
|||
_customSize = esize;
|
||||
_centerSizeMultiplier = _customSize / float64(size);
|
||||
aroundAnimation = owner->chooseGenericAnimation(document);
|
||||
} else if (args.id.paid()) {
|
||||
const auto fake = owner->lookupPaid();
|
||||
centerIcon = fake->centerIcon;
|
||||
aroundAnimation = fake->aroundAnimation;
|
||||
_centerSizeMultiplier = 1.;// fake->centerIcon ? 1. : 0.5;
|
||||
} else {
|
||||
const auto i = ranges::find(list, args.id, &::Data::Reaction::id);
|
||||
if (i == end(list)/* || !i->centerIcon*/) {
|
||||
|
|
Loading…
Add table
Reference in a new issue