mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Initial stake dice game implementation.
This commit is contained in:
@@ -1642,6 +1642,8 @@ PRIVATE
|
||||
tde2e/tde2e_integration.h
|
||||
ui/boxes/edit_invite_link_session.cpp
|
||||
ui/boxes/edit_invite_link_session.h
|
||||
ui/boxes/emoji_stake_box.cpp
|
||||
ui/boxes/emoji_stake_box.h
|
||||
ui/boxes/peer_qr_box.cpp
|
||||
ui/boxes/peer_qr_box.h
|
||||
ui/chat/attach/attach_item_single_file_preview.cpp
|
||||
|
||||
@@ -2467,6 +2467,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_not_enough_funds" = "Transaction failed.";
|
||||
"lng_you_paid_stars#one" = "You paid {count} Star.";
|
||||
"lng_you_paid_stars#other" = "You paid {count} Stars.";
|
||||
"lng_action_stake_game_loading" = "Trying luck in a game...";
|
||||
"lng_action_stake_game_nothing" = "{from} didn't win anything";
|
||||
"lng_action_stake_game_nothing_you" = "You didn't win anything";
|
||||
"lng_action_stake_game_won" = "{from} won {amount}";
|
||||
"lng_action_stake_game_won_you" = "You won {amount}";
|
||||
|
||||
"lng_stake_game_title" = "Emoji Stake";
|
||||
"lng_stake_game_beta" = "Beta";
|
||||
"lng_stake_game_about" = "A limited play-test of the upcoming emoji mini-game platform for a small group of users.";
|
||||
"lng_stake_game_results" = "Results and Returns";
|
||||
"lng_stake_game_resets" = "A streak resets after 3 sixes or a stake change.";
|
||||
"lng_stake_game_your" = "Your stake";
|
||||
"lng_stake_game_save_and_roll" = "Save and Roll";
|
||||
|
||||
"lng_you_joined_group" = "You joined this group";
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ struct SendOptions {
|
||||
TimeId scheduleRepeatPeriod = 0;
|
||||
BusinessShortcutId shortcutId = 0;
|
||||
EffectId effectId = 0;
|
||||
QByteArray stakeSeedHash;
|
||||
int64 stakeNanoTon = 0;
|
||||
int starsApproved = 0;
|
||||
bool silent = false;
|
||||
bool handleSupportSwitch = false;
|
||||
|
||||
@@ -347,7 +347,7 @@ bool SendDice(MessageToSend &message) {
|
||||
|| !message.textWithTags.tags.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
auto &config = message.action.history->session().appConfig();
|
||||
//auto &config = message.action.history->session().appConfig();
|
||||
static const auto hardcoded = std::vector<QString>{
|
||||
Stickers::DicePacks::kDiceString,
|
||||
Stickers::DicePacks::kDartString,
|
||||
@@ -356,9 +356,9 @@ bool SendDice(MessageToSend &message) {
|
||||
Stickers::DicePacks::kFballString + QChar(0xFE0F),
|
||||
Stickers::DicePacks::kBballString,
|
||||
};
|
||||
const auto list = config.get<std::vector<QString>>(
|
||||
const auto list = hardcoded/*config.get<std::vector<QString>>(
|
||||
"emojies_send_dice",
|
||||
hardcoded);
|
||||
hardcoded)*/; AssertIsDebug();
|
||||
const auto emoji = full.toString();
|
||||
if (!ranges::contains(list, emoji)) {
|
||||
return false;
|
||||
@@ -372,7 +372,6 @@ bool SendDice(MessageToSend &message) {
|
||||
message.action.clearDraft = false;
|
||||
message.action.generateLocal = true;
|
||||
|
||||
|
||||
auto &action = message.action;
|
||||
api->sendAction(action);
|
||||
|
||||
@@ -428,6 +427,11 @@ bool SendDice(MessageToSend &message) {
|
||||
|
||||
session->data().registerMessageRandomId(randomId, newId);
|
||||
|
||||
auto seed = QByteArray(32, Qt::Uninitialized);
|
||||
base::RandomFill(bytes::make_detached_span(seed));
|
||||
const auto stake = action.options.stakeSeedHash.isEmpty()
|
||||
? 0
|
||||
: action.options.stakeNanoTon;
|
||||
history->addNewLocalMessage({
|
||||
.id = newId.msg,
|
||||
.flags = flags,
|
||||
@@ -440,10 +444,12 @@ bool SendDice(MessageToSend &message) {
|
||||
.effectId = action.options.effectId,
|
||||
.suggest = HistoryMessageSuggestInfo(action.options),
|
||||
}, TextWithEntities(), MTP_messageMediaDice(
|
||||
MTP_flags(0),
|
||||
MTP_flags(stake
|
||||
? MTPDmessageMediaDice::Flag::f_game_outcome
|
||||
: MTPDmessageMediaDice::Flag()),
|
||||
MTP_int(0),
|
||||
MTP_string(emoji),
|
||||
MTPmessages_EmojiGameOutcome()));
|
||||
MTP_messages_emojiGameOutcome(MTP_bytes(seed), MTP_long(0))));
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
action.replyTo,
|
||||
@@ -452,7 +458,12 @@ bool SendDice(MessageToSend &message) {
|
||||
MTP_flags(sendFlags),
|
||||
peer->input(),
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
MTP_inputMediaDice(MTP_string(emoji)),
|
||||
(stake
|
||||
? MTP_inputMediaStakeDice(
|
||||
MTP_bytes(action.options.stakeSeedHash),
|
||||
MTP_long(stake),
|
||||
MTP_bytes(seed))
|
||||
: MTP_inputMediaDice(MTP_string(emoji))),
|
||||
MTP_string(),
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
|
||||
@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mtproto/mtp_instance.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "mtproto/mtproto_dc_options.h"
|
||||
#include "chat_helpers/stickers_dice_pack.h"
|
||||
#include "data/business/data_shortcut_messages.h"
|
||||
#include "data/components/credits.h"
|
||||
#include "data/components/gift_auctions.h"
|
||||
@@ -2677,6 +2678,11 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
const auto &data = update.c_updateStarGiftAuctionUserState();
|
||||
_session->giftAuctions().apply(data);
|
||||
} break;
|
||||
|
||||
case mtpc_updateEmojiGameInfo: {
|
||||
const auto &data = update.c_updateEmojiGameInfo();
|
||||
_session->diceStickersPacks().apply(data);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -548,6 +548,8 @@ void ApiWrap::sendMessageFail(
|
||||
}
|
||||
}
|
||||
peer->updateFull();
|
||||
} else if (show) {
|
||||
show->showToast(error);
|
||||
}
|
||||
if (const auto item = _session->data().message(itemId)) {
|
||||
Assert(randomId != 0);
|
||||
|
||||
@@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "settings/settings_credits_graphics.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/boxes/emoji_stake_box.h" // AddStarsInputField
|
||||
#include "ui/controls/button_labels.h"
|
||||
#include "ui/controls/feature_list.h"
|
||||
#include "ui/controls/table_rows.h"
|
||||
@@ -603,7 +604,7 @@ void EditCustomBid(
|
||||
|
||||
box->addTopButton(st::boxTitleClose, [=] { box->closeBox(); });
|
||||
|
||||
const auto starsField = HistoryView::AddStarsInputField(container, {
|
||||
const auto starsField = AddStarsInputField(container, {
|
||||
.value = current,
|
||||
});
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "main/main_session.h"
|
||||
#include "chat_helpers/stickers_lottie.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_document.h"
|
||||
#include "base/unixtime.h"
|
||||
@@ -144,4 +145,54 @@ DocumentData *DicePacks::lookup(const QString &emoji, int value) {
|
||||
).first->second->lookup(value);
|
||||
}
|
||||
|
||||
void DicePacks::resolveGameOptions(
|
||||
Fn<void(const Data::DiceGameOptions &)> done) {
|
||||
_resolveGameOptionsCallback = std::move(done);
|
||||
if (_resolveGameOptionsRequestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
_resolveGameOptionsRequestId = _session->api().request(
|
||||
MTPmessages_GetEmojiGameInfo()
|
||||
).done([=](const MTPmessages_EmojiGameInfo &result) {
|
||||
_resolveGameOptionsRequestId = 0;
|
||||
if (const auto onstack = base::take(_resolveGameOptionsCallback)) {
|
||||
onstack(result.match([&](
|
||||
const MTPDmessages_emojiGameUnavailable &) {
|
||||
return Data::DiceGameOptions();
|
||||
}, [&](const MTPDmessages_emojiGameDiceInfo &data) {
|
||||
auto jackpot = 0;
|
||||
auto rewards = std::array<int, 6>{};
|
||||
const auto ¶ms = data.vparams().v;
|
||||
const auto count = int(params.size());
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
if (i < 6) {
|
||||
rewards[i] = params[i].v;
|
||||
} else if (i == 6) {
|
||||
jackpot = params[i].v;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Data::DiceGameOptions{
|
||||
.seedHash = data.vgame_hash().v,
|
||||
.previousSteakNanoTon = int64(data.vprev_stake().v),
|
||||
.milliRewards = rewards,
|
||||
.jackpotMilliReward = jackpot,
|
||||
.currentStreak = data.vcurrent_streak().v,
|
||||
.playsLeft = data.vplays_left().value_or_empty(),
|
||||
};
|
||||
}));
|
||||
}
|
||||
}).fail([=] {
|
||||
_resolveGameOptionsRequestId = 0;
|
||||
if (const auto onstack = base::take(_resolveGameOptionsCallback)) {
|
||||
onstack({});
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void DicePacks::apply(const MTPDupdateEmojiGameInfo &update) {
|
||||
}
|
||||
|
||||
} // namespace Stickers
|
||||
|
||||
@@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class DocumentData;
|
||||
|
||||
namespace Data {
|
||||
struct DiceGameOptions;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@@ -51,11 +55,17 @@ public:
|
||||
|
||||
[[nodiscard]] DocumentData *lookup(const QString &emoji, int value);
|
||||
|
||||
void resolveGameOptions(Fn<void(const Data::DiceGameOptions &)> done);
|
||||
void apply(const MTPDupdateEmojiGameInfo &update);
|
||||
|
||||
private:
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
base::flat_map<QString, std::unique_ptr<DicePack>> _packs;
|
||||
|
||||
mtpRequestId _resolveGameOptionsRequestId = 0;
|
||||
Fn<void(const Data::DiceGameOptions &)> _resolveGameOptionsCallback;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Stickers
|
||||
|
||||
@@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/media/history_view_unique_gift.h"
|
||||
#include "history/view/media/history_view_userpic_suggestion.h"
|
||||
#include "dialogs/ui/dialogs_message_view.h"
|
||||
#include "ui/boxes/emoji_stake_box.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/effects/spoiler_mess.h"
|
||||
#include "ui/text/format_song_document_name.h"
|
||||
@@ -2420,14 +2421,19 @@ std::unique_ptr<HistoryView::Media> MediaTodoList::createView(
|
||||
replacing);
|
||||
}
|
||||
|
||||
MediaDice::MediaDice(not_null<HistoryItem*> parent, QString emoji, int value)
|
||||
MediaDice::MediaDice(
|
||||
not_null<HistoryItem*> parent,
|
||||
DiceGameOutcome outcome,
|
||||
QString emoji,
|
||||
int value)
|
||||
: Media(parent)
|
||||
, _outcome(outcome)
|
||||
, _emoji(emoji)
|
||||
, _value(value) {
|
||||
}
|
||||
|
||||
std::unique_ptr<Media> MediaDice::clone(not_null<HistoryItem*> parent) {
|
||||
return std::make_unique<MediaDice>(parent, _emoji, _value);
|
||||
return std::make_unique<MediaDice>(parent, _outcome, _emoji, _value);
|
||||
}
|
||||
|
||||
QString MediaDice::emoji() const {
|
||||
@@ -2438,6 +2444,10 @@ int MediaDice::value() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
DiceGameOutcome MediaDice::outcome() const {
|
||||
return _outcome;
|
||||
}
|
||||
|
||||
bool MediaDice::allowsRevoke(TimeId now) const {
|
||||
const auto peer = parent()->history()->peer;
|
||||
if (peer->isSelf() || !peer->isUser()) {
|
||||
@@ -2470,8 +2480,18 @@ bool MediaDice::updateSentMedia(const MTPMessageMedia &media) {
|
||||
if (media.type() != mtpc_messageMediaDice) {
|
||||
return false;
|
||||
}
|
||||
_value = media.c_messageMediaDice().vvalue().v;
|
||||
parent()->history()->owner().requestItemRepaint(parent());
|
||||
const auto &data = media.c_messageMediaDice();
|
||||
_value = data.vvalue().v;
|
||||
if (const auto outcome = data.vgame_outcome()) {
|
||||
const auto &data = outcome->data();
|
||||
_outcome = Data::DiceGameOutcome{
|
||||
.nanoTon = int64(data.vton_amount().v),
|
||||
.seed = data.vseed().v,
|
||||
};
|
||||
} else {
|
||||
_outcome = {};
|
||||
}
|
||||
parent()->history()->owner().notifyItemDataChange(parent());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2504,6 +2524,8 @@ ClickHandlerPtr MediaDice::MakeHandler(
|
||||
}
|
||||
};
|
||||
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
const auto weak = my.sessionWindow;
|
||||
auto config = Ui::Toast::Config{
|
||||
.text = { tr::lng_about_random(tr::now, lt_emoji, emoji) },
|
||||
.st = &st::historyDiceToast,
|
||||
@@ -2517,13 +2539,38 @@ ClickHandlerPtr MediaDice::MakeHandler(
|
||||
config.filter = crl::guard(&history->session(), [=](
|
||||
const ClickHandlerPtr &handler,
|
||||
Qt::MouseButton button) {
|
||||
const auto pack = &history->session().diceStickersPacks();
|
||||
if (button == Qt::LeftButton && !ShownToast.empty()) {
|
||||
auto message = Api::MessageToSend(
|
||||
Api::SendAction(history));
|
||||
message.action.clearDraft = false;
|
||||
message.textWithTags.text = emoji;
|
||||
pack->resolveGameOptions([=](
|
||||
const Data::DiceGameOptions &options) {
|
||||
const auto window = weak.get();
|
||||
const auto seedHash = options.seedHash;
|
||||
const auto sendWithStake = [=](int64 stakeNanoTon) {
|
||||
auto message = Api::MessageToSend(
|
||||
Api::SendAction(history));
|
||||
message.textWithTags.text = emoji;
|
||||
|
||||
Api::SendDice(message);
|
||||
auto &action = message.action;
|
||||
action.clearDraft = false;
|
||||
|
||||
auto &options = action.options;
|
||||
options.stakeNanoTon = stakeNanoTon;
|
||||
options.stakeSeedHash = seedHash;
|
||||
|
||||
Api::SendDice(message);
|
||||
};
|
||||
if (!options || !window) {
|
||||
sendWithStake(0);
|
||||
} else {
|
||||
window->show(Ui::MakeEmojiGameStakeBox({
|
||||
.session = &window->session(),
|
||||
.currentStake = options.previousSteakNanoTon,
|
||||
.milliRewards = options.milliRewards,
|
||||
.jackpotMilliReward = options.jackpotMilliReward,
|
||||
.submit = sendWithStake,
|
||||
}));
|
||||
}
|
||||
});
|
||||
HideExisting();
|
||||
}
|
||||
return false;
|
||||
@@ -2531,8 +2578,6 @@ ClickHandlerPtr MediaDice::MakeHandler(
|
||||
}
|
||||
|
||||
HideExisting();
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
const auto weak = my.sessionWindow;
|
||||
if (const auto strong = weak.get()) {
|
||||
ShownToast = strong->showToast(std::move(config));
|
||||
} else {
|
||||
|
||||
@@ -653,14 +653,41 @@ private:
|
||||
|
||||
};
|
||||
|
||||
struct DiceGameOptions {
|
||||
QByteArray seedHash;
|
||||
int64 previousSteakNanoTon = 0;
|
||||
std::array<int, 6> milliRewards;
|
||||
int jackpotMilliReward = 0;
|
||||
int currentStreak = 0;
|
||||
int playsLeft = 0;
|
||||
|
||||
explicit operator bool() const {
|
||||
return !seedHash.isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
struct DiceGameOutcome {
|
||||
int64 nanoTon = 0;
|
||||
QByteArray seed;
|
||||
|
||||
explicit operator bool() const {
|
||||
return !seed.isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
class MediaDice final : public Media {
|
||||
public:
|
||||
MediaDice(not_null<HistoryItem*> parent, QString emoji, int value);
|
||||
MediaDice(
|
||||
not_null<HistoryItem*> parent,
|
||||
DiceGameOutcome outcome,
|
||||
QString emoji,
|
||||
int value);
|
||||
|
||||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||
|
||||
[[nodiscard]] QString emoji() const;
|
||||
[[nodiscard]] int value() const;
|
||||
[[nodiscard]] DiceGameOutcome outcome() const;
|
||||
|
||||
bool allowsRevoke(TimeId now) const override;
|
||||
TextWithEntities notificationText() const override;
|
||||
@@ -681,6 +708,7 @@ public:
|
||||
const QString &emoji);
|
||||
|
||||
private:
|
||||
DiceGameOutcome _outcome;
|
||||
QString _emoji;
|
||||
int _value = 0;
|
||||
|
||||
|
||||
@@ -366,8 +366,15 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
|
||||
item,
|
||||
item->history()->owner().processTodoList(item->fullId(), media));
|
||||
}, [&](const MTPDmessageMediaDice &media) -> Result {
|
||||
auto outcome = Data::DiceGameOutcome();
|
||||
if (const auto game = media.vgame_outcome()) {
|
||||
const auto &data = game->data();
|
||||
outcome.seed = data.vseed().v;
|
||||
outcome.nanoTon = data.vton_amount().v;
|
||||
}
|
||||
return std::make_unique<Data::MediaDice>(
|
||||
item,
|
||||
outcome,
|
||||
qs(media.vemoticon()),
|
||||
media.vvalue().v);
|
||||
}, [&](const MTPDmessageMediaStory &media) -> Result {
|
||||
|
||||
@@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/boxes/choose_date_time.h"
|
||||
#include "ui/boxes/emoji_stake_box.h"
|
||||
#include "ui/boxes/single_choice_box.h"
|
||||
#include "ui/controls/ton_common.h"
|
||||
#include "ui/widgets/fields/number_input.h"
|
||||
@@ -75,19 +76,6 @@ namespace {
|
||||
}));
|
||||
}
|
||||
|
||||
[[nodiscard]] not_null<Ui::RpWidget*> AddMoneyInputIcon(
|
||||
not_null<QWidget*> parent,
|
||||
Ui::Text::PaletteDependentEmoji emoji) {
|
||||
auto helper = Ui::Text::CustomEmojiHelper();
|
||||
auto text = helper.paletteDependent(std::move(emoji));
|
||||
return Ui::CreateChild<Ui::FlatLabel>(
|
||||
parent,
|
||||
rpl::single(std::move(text)),
|
||||
st::defaultFlatLabel,
|
||||
st::defaultPopupMenu,
|
||||
helper.context());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ChooseSuggestTimeBox(
|
||||
@@ -153,60 +141,6 @@ void AddApproximateUsd(
|
||||
usd->widthValue() | rpl::on_next(move, usd->lifetime());
|
||||
}
|
||||
|
||||
not_null<Ui::NumberInput*> AddStarsInputField(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
StarsInputFieldArgs &&args) {
|
||||
const auto wrap = container->add(
|
||||
object_ptr<Ui::FixedHeightWidget>(
|
||||
container,
|
||||
st::editTagField.heightMin),
|
||||
st::boxRowPadding);
|
||||
const auto result = Ui::CreateChild<Ui::NumberInput>(
|
||||
wrap,
|
||||
st::editTagField,
|
||||
rpl::single(u"0"_q),
|
||||
args.value ? QString::number(*args.value) : QString(),
|
||||
args.max ? args.max : std::numeric_limits<int>::max());
|
||||
const auto icon = AddMoneyInputIcon(
|
||||
result,
|
||||
Ui::Earn::IconCreditsEmoji());
|
||||
|
||||
wrap->widthValue() | rpl::on_next([=](int width) {
|
||||
icon->move(st::starsFieldIconPosition);
|
||||
result->move(0, 0);
|
||||
result->resize(width, result->height());
|
||||
wrap->resize(width, result->height());
|
||||
}, wrap->lifetime());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
not_null<Ui::InputField*> AddTonInputField(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
TonInputFieldArgs &&args) {
|
||||
const auto wrap = container->add(
|
||||
object_ptr<Ui::FixedHeightWidget>(
|
||||
container,
|
||||
st::editTagField.heightMin),
|
||||
st::boxRowPadding);
|
||||
const auto result = Ui::CreateTonAmountInput(
|
||||
wrap,
|
||||
rpl::single('0' + Ui::TonAmountSeparator() + '0'),
|
||||
args.value);
|
||||
const auto icon = AddMoneyInputIcon(
|
||||
result,
|
||||
Ui::Earn::IconCurrencyEmoji());
|
||||
|
||||
wrap->widthValue() | rpl::on_next([=](int width) {
|
||||
icon->move(st::tonFieldIconPosition);
|
||||
result->move(0, 0);
|
||||
result->resize(width, result->height());
|
||||
wrap->resize(width, result->height());
|
||||
}, wrap->lifetime());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
StarsTonPriceInput AddStarsTonPriceInput(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
StarsTonPriceArgs &&args) {
|
||||
|
||||
@@ -51,21 +51,6 @@ void ChooseSuggestTimeBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
SuggestTimeBoxArgs &&args);
|
||||
|
||||
struct StarsInputFieldArgs {
|
||||
std::optional<int64> value;
|
||||
int64 max = 0;
|
||||
};
|
||||
[[nodiscard]] not_null<Ui::NumberInput*> AddStarsInputField(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
StarsInputFieldArgs &&args);
|
||||
|
||||
struct TonInputFieldArgs {
|
||||
int64 value = 0;
|
||||
};
|
||||
[[nodiscard]] not_null<Ui::InputField*> AddTonInputField(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
TonInputFieldArgs &&args);
|
||||
|
||||
struct StarsTonPriceInput {
|
||||
Fn<void()> focusCallback;
|
||||
Fn<std::optional<CreditsAmount>()> computeResult;
|
||||
|
||||
@@ -3278,8 +3278,9 @@ bool Message::updateBottomInfo() {
|
||||
void Message::itemDataChanged() {
|
||||
const auto infoChanged = updateBottomInfo();
|
||||
const auto reactionsChanged = updateReactions();
|
||||
|
||||
if (infoChanged || reactionsChanged) {
|
||||
const auto media = this->media();
|
||||
const auto mediaChanged = media && media->updateItemData();
|
||||
if (infoChanged || reactionsChanged || mediaChanged) {
|
||||
history()->owner().requestViewResize(this);
|
||||
} else {
|
||||
repaint();
|
||||
|
||||
@@ -12,7 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
@@ -42,10 +44,53 @@ Dice::Dice(not_null<Element*> parent, not_null<Data::MediaDice*> dice)
|
||||
if (_showLastFrame) {
|
||||
_drawingEnd = true;
|
||||
}
|
||||
|
||||
if (const auto outcome = _dice->outcome()) {
|
||||
_outcomeSet = true;
|
||||
_outcomeNanoTon = outcome.nanoTon;
|
||||
updateOutcomeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
Dice::~Dice() = default;
|
||||
|
||||
void Dice::updateOutcomeMessage() {
|
||||
const auto item = _parent->data();
|
||||
const auto from = item->from();
|
||||
const auto out = item->out() || from->isSelf();
|
||||
const auto amount = tr::marked(QString::fromUtf8("\xf0\x9f\x92\x8e")
|
||||
+ " "
|
||||
+ QString::number(_outcomeNanoTon / 1e9));
|
||||
const auto text = _outcomeNanoTon
|
||||
? (out
|
||||
? tr::lng_action_stake_game_won_you(
|
||||
tr::now,
|
||||
lt_amount,
|
||||
amount,
|
||||
tr::marked)
|
||||
: tr::lng_action_stake_game_won(
|
||||
tr::now,
|
||||
lt_from,
|
||||
tr::link(st::wrap_rtl(from->name()), 1),
|
||||
lt_amount,
|
||||
amount,
|
||||
tr::marked))
|
||||
: !_dice->value()
|
||||
? tr::lng_action_stake_game_loading(tr::now, tr::marked)
|
||||
: (out
|
||||
? tr::lng_action_stake_game_nothing_you(tr::now, tr::marked)
|
||||
: tr::lng_action_stake_game_nothing(
|
||||
tr::now,
|
||||
lt_from,
|
||||
tr::link(st::wrap_rtl(from->name()), 1),
|
||||
tr::marked));
|
||||
auto prepared = PreparedServiceText{ text };
|
||||
if (!out) {
|
||||
prepared.links.push_back(from->createOpenLink());
|
||||
}
|
||||
_parent->setServicePreMessage(prepared, _link);
|
||||
}
|
||||
|
||||
QSize Dice::countOptimalSize() {
|
||||
return _start ? _start->countOptimalSize() : Sticker::EmojiSize();
|
||||
}
|
||||
@@ -54,6 +99,19 @@ ClickHandlerPtr Dice::link() {
|
||||
return _link;
|
||||
}
|
||||
|
||||
bool Dice::updateItemData() {
|
||||
const auto outcome = _dice->outcome();
|
||||
const auto outcomeSet = !!outcome;
|
||||
const auto outcomeNanoTon = outcomeSet ? outcome.nanoTon : 0;
|
||||
if (_outcomeSet == outcomeSet && _outcomeNanoTon == outcomeNanoTon) {
|
||||
return false;
|
||||
}
|
||||
_outcomeSet = outcomeSet;
|
||||
_outcomeNanoTon = outcomeNanoTon;
|
||||
updateOutcomeMessage();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Dice::draw(Painter &p, const PaintContext &context, const QRect &r) {
|
||||
if (!_start) {
|
||||
if (const auto document = Lookup(_parent, _dice->emoji(), 0)) {
|
||||
|
||||
@@ -42,14 +42,20 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool updateItemData() override;
|
||||
|
||||
private:
|
||||
void updateOutcomeMessage();
|
||||
|
||||
const not_null<Element*> _parent;
|
||||
const not_null<Data::MediaDice*> _dice;
|
||||
ClickHandlerPtr _link;
|
||||
std::optional<Sticker> _start;
|
||||
std::optional<Sticker> _end;
|
||||
int64 _outcomeNanoTon = false;
|
||||
mutable bool _showLastFrame = false;
|
||||
mutable bool _drawingEnd = false;
|
||||
bool _outcomeSet = false;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -354,6 +354,9 @@ public:
|
||||
[[nodiscard]] virtual QMargins bubbleRollRepaintMargins() const {
|
||||
return QMargins();
|
||||
}
|
||||
virtual bool updateItemData() {
|
||||
return false;
|
||||
}
|
||||
virtual void paintBubbleFireworks(
|
||||
Painter &p,
|
||||
const QRect &bubble,
|
||||
|
||||
@@ -58,6 +58,9 @@ public:
|
||||
virtual bool hasTextForCopy() const {
|
||||
return false;
|
||||
}
|
||||
virtual bool updateItemData() {
|
||||
return false;
|
||||
}
|
||||
virtual ~Content() = default;
|
||||
};
|
||||
|
||||
@@ -93,6 +96,10 @@ public:
|
||||
std::optional<int> reactionButtonCenterOverride() const override;
|
||||
QPoint resolveCustomInfoRightBottom() const override;
|
||||
|
||||
bool updateItemData() override {
|
||||
return _content->updateItemData();
|
||||
}
|
||||
|
||||
void stickerClearLoopPlayed() override {
|
||||
_content->stickerClearLoopPlayed();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
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 "ui/boxes/emoji_stake_box.h"
|
||||
|
||||
#include "base/object_ptr.h"
|
||||
#include "data/components/credits.h"
|
||||
#include "info/channel_statistics/earn/earn_icons.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "lottie/lottie_icon.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_common.h" // CreateLottieIcon
|
||||
#include "settings/settings_credits_graphics.h" // AddBalanceWidget
|
||||
#include "ui/controls/ton_common.h"
|
||||
#include "ui/text/custom_emoji_helper.h"
|
||||
#include "ui/text/custom_emoji_text_badge.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "ui/widgets/fields/number_input.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "styles/style_calls.h" // confcallJoinBox
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_credits.h" // creditsHistoryRightSkip
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_settings.h" // settingsCloudPasswordIconSize
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] not_null<RpWidget*> AddMoneyInputIcon(
|
||||
not_null<QWidget*> parent,
|
||||
Text::PaletteDependentEmoji emoji) {
|
||||
auto helper = Text::CustomEmojiHelper();
|
||||
auto text = helper.paletteDependent(std::move(emoji));
|
||||
return CreateChild<FlatLabel>(
|
||||
parent,
|
||||
rpl::single(std::move(text)),
|
||||
st::defaultFlatLabel,
|
||||
st::defaultPopupMenu,
|
||||
helper.context());
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<RpWidget> MakeLogo(not_null<GenericBox*> box) {
|
||||
const auto &size = st::settingsCloudPasswordIconSize;
|
||||
auto icon = Settings::CreateLottieIcon(
|
||||
box->verticalLayout(),
|
||||
{ .name = u"dice_idle"_q, .sizeOverride = { size, size } },
|
||||
st::settingLocalPasscodeIconPadding);
|
||||
const auto animate = std::move(icon.animate);
|
||||
box->showFinishes() | rpl::take(1) | rpl::on_next([=] {
|
||||
animate(anim::repeat::loop);
|
||||
}, box->lifetime());
|
||||
return std::move(icon.widget);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
not_null<NumberInput*> AddStarsInputField(
|
||||
not_null<VerticalLayout*> container,
|
||||
StarsInputFieldArgs &&args) {
|
||||
const auto wrap = container->add(
|
||||
object_ptr<FixedHeightWidget>(
|
||||
container,
|
||||
st::editTagField.heightMin),
|
||||
st::boxRowPadding);
|
||||
const auto result = CreateChild<NumberInput>(
|
||||
wrap,
|
||||
st::editTagField,
|
||||
rpl::single(u"0"_q),
|
||||
args.value ? QString::number(*args.value) : QString(),
|
||||
args.max ? args.max : std::numeric_limits<int>::max());
|
||||
const auto icon = AddMoneyInputIcon(
|
||||
result,
|
||||
Earn::IconCreditsEmoji());
|
||||
|
||||
wrap->widthValue() | rpl::on_next([=](int width) {
|
||||
icon->move(st::starsFieldIconPosition);
|
||||
result->move(0, 0);
|
||||
result->resize(width, result->height());
|
||||
wrap->resize(width, result->height());
|
||||
}, wrap->lifetime());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
not_null<InputField*> AddTonInputField(
|
||||
not_null<VerticalLayout*> container,
|
||||
TonInputFieldArgs &&args) {
|
||||
const auto wrap = container->add(
|
||||
object_ptr<FixedHeightWidget>(
|
||||
container,
|
||||
st::editTagField.heightMin),
|
||||
st::boxRowPadding);
|
||||
const auto result = CreateTonAmountInput(
|
||||
wrap,
|
||||
rpl::single('0' + TonAmountSeparator() + '0'),
|
||||
args.value);
|
||||
const auto icon = AddMoneyInputIcon(
|
||||
result,
|
||||
Earn::IconCurrencyEmoji());
|
||||
|
||||
wrap->widthValue() | rpl::on_next([=](int width) {
|
||||
icon->move(st::tonFieldIconPosition);
|
||||
result->move(0, 0);
|
||||
result->resize(width, result->height());
|
||||
wrap->resize(width, result->height());
|
||||
}, wrap->lifetime());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void EmojiGameStakeBox(
|
||||
not_null<GenericBox*> box,
|
||||
EmojiGameStakeArgs &&args) {
|
||||
box->setStyle(st::confcallJoinBox);
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setNoContentMargin(true);
|
||||
|
||||
box->addRow(
|
||||
MakeLogo(box),
|
||||
st::boxRowPadding + st::confcallLinkHeaderIconPadding,
|
||||
style::al_top);
|
||||
|
||||
auto helper = Text::CustomEmojiHelper();
|
||||
const auto beta = helper.paletteDependent(
|
||||
Text::CustomEmojiTextBadge(
|
||||
tr::lng_stake_game_beta(tr::now),
|
||||
st::customEmojiTextBadge));
|
||||
auto title = tr::lng_stake_game_title(
|
||||
tr::marked
|
||||
) | rpl::map([=](TextWithEntities &&text) {
|
||||
return text.append(' ').append(beta);
|
||||
});
|
||||
box->addRow(
|
||||
object_ptr<FlatLabel>(
|
||||
box,
|
||||
std::move(title),
|
||||
st::boxTitle,
|
||||
st::defaultPopupMenu,
|
||||
helper.context()),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding,
|
||||
style::al_top);
|
||||
box->addRow(
|
||||
object_ptr<FlatLabel>(
|
||||
box,
|
||||
tr::lng_stake_game_about(tr::rich),
|
||||
st::confcallLinkCenteredText),
|
||||
st::boxRowPadding,
|
||||
style::al_top
|
||||
)->setTryMakeSimilarLines(true);
|
||||
|
||||
const auto container = box->verticalLayout();
|
||||
|
||||
AddSubsectionTitle(container, tr::lng_stake_game_results());
|
||||
|
||||
const auto close = CreateChild<IconButton>(
|
||||
container,
|
||||
st::boxTitleClose);
|
||||
close->setClickedCallback([=] { box->closeBox(); });
|
||||
container->widthValue() | rpl::on_next([=](int) {
|
||||
close->moveToRight(0, 0);
|
||||
}, close->lifetime());
|
||||
|
||||
const auto session = args.session;
|
||||
session->credits().tonLoad(true);
|
||||
const auto balance = Settings::AddBalanceWidget(
|
||||
container,
|
||||
session,
|
||||
session->credits().tonBalanceValue(),
|
||||
false);
|
||||
rpl::combine(
|
||||
balance->sizeValue(),
|
||||
container->sizeValue()
|
||||
) | rpl::on_next([=](const QSize &, const QSize &) {
|
||||
balance->moveToLeft(
|
||||
st::creditsHistoryRightSkip * 2,
|
||||
st::creditsHistoryRightSkip);
|
||||
balance->update();
|
||||
}, balance->lifetime());
|
||||
|
||||
box->addRow(
|
||||
object_ptr<FlatLabel>(
|
||||
box,
|
||||
tr::lng_stake_game_resets(tr::rich),
|
||||
st::confcallLinkCenteredText),
|
||||
st::boxRowPadding,
|
||||
style::al_top
|
||||
)->setTryMakeSimilarLines(true);
|
||||
|
||||
AddSubsectionTitle(container, tr::lng_stake_game_your());
|
||||
const auto field = AddTonInputField(container, {
|
||||
.value = args.currentStake,
|
||||
});
|
||||
|
||||
const auto submit = args.submit;
|
||||
const auto button = box->addButton(rpl::single(QString()), [=] {
|
||||
const auto text = field->getLastText();
|
||||
const auto now = Ui::ParseTonAmountString(text);
|
||||
if (!now) {
|
||||
field->showError();
|
||||
} else {
|
||||
box->closeBox();
|
||||
submit(*now);
|
||||
}
|
||||
});
|
||||
|
||||
button->setText(tr::lng_stake_game_save_and_roll(
|
||||
) | rpl::map([=](QString text) {
|
||||
return tr::marked(
|
||||
QString::fromUtf8("\xf0\x9f\x8e\xb2")
|
||||
).append(' ').append(text);
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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 "ui/layers/generic_box.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class InputField;
|
||||
class NumberInput;
|
||||
class VerticalLayout;
|
||||
|
||||
struct StarsInputFieldArgs {
|
||||
std::optional<int64> value;
|
||||
int64 max = 0;
|
||||
};
|
||||
[[nodiscard]] not_null<NumberInput*> AddStarsInputField(
|
||||
not_null<VerticalLayout*> container,
|
||||
StarsInputFieldArgs &&args);
|
||||
|
||||
struct TonInputFieldArgs {
|
||||
int64 value = 0;
|
||||
};
|
||||
[[nodiscard]] not_null<InputField*> AddTonInputField(
|
||||
not_null<VerticalLayout*> container,
|
||||
TonInputFieldArgs &&args);
|
||||
|
||||
struct EmojiGameStakeArgs {
|
||||
not_null<Main::Session*> session;
|
||||
int64 currentStake = 0;
|
||||
std::array<int, 6> milliRewards;
|
||||
int jackpotMilliReward = 0;
|
||||
Fn<void(int64)> submit;
|
||||
};
|
||||
void EmojiGameStakeBox(not_null<GenericBox*> box, EmojiGameStakeArgs &&args);
|
||||
[[nodiscard]] inline object_ptr<GenericBox> MakeEmojiGameStakeBox(
|
||||
EmojiGameStakeArgs &&args) {
|
||||
return Box(EmojiGameStakeBox, std::move(args));
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
Reference in New Issue
Block a user