diff --git a/Telegram/Resources/animations/dice/dice_1.tgs b/Telegram/Resources/animations/dice/dice_1.tgs
deleted file mode 100644
index 36506f7108..0000000000
Binary files a/Telegram/Resources/animations/dice/dice_1.tgs and /dev/null differ
diff --git a/Telegram/Resources/animations/dice/dice_2.tgs b/Telegram/Resources/animations/dice/dice_2.tgs
deleted file mode 100644
index ac0de6ff5a..0000000000
Binary files a/Telegram/Resources/animations/dice/dice_2.tgs and /dev/null differ
diff --git a/Telegram/Resources/animations/dice/dice_3.tgs b/Telegram/Resources/animations/dice/dice_3.tgs
deleted file mode 100644
index 1362b087fe..0000000000
Binary files a/Telegram/Resources/animations/dice/dice_3.tgs and /dev/null differ
diff --git a/Telegram/Resources/animations/dice/dice_4.tgs b/Telegram/Resources/animations/dice/dice_4.tgs
deleted file mode 100644
index 60b7c6d8ce..0000000000
Binary files a/Telegram/Resources/animations/dice/dice_4.tgs and /dev/null differ
diff --git a/Telegram/Resources/animations/dice/dice_5.tgs b/Telegram/Resources/animations/dice/dice_5.tgs
deleted file mode 100644
index 77bb708982..0000000000
Binary files a/Telegram/Resources/animations/dice/dice_5.tgs and /dev/null differ
diff --git a/Telegram/Resources/art/dice/dice1.svg b/Telegram/Resources/art/dice/dice1.svg
new file mode 100644
index 0000000000..4ab6abbe61
--- /dev/null
+++ b/Telegram/Resources/art/dice/dice1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Telegram/Resources/art/dice/dice2.svg b/Telegram/Resources/art/dice/dice2.svg
new file mode 100644
index 0000000000..1e9e0c8534
--- /dev/null
+++ b/Telegram/Resources/art/dice/dice2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Telegram/Resources/art/dice/dice3.svg b/Telegram/Resources/art/dice/dice3.svg
new file mode 100644
index 0000000000..84ff3fcc13
--- /dev/null
+++ b/Telegram/Resources/art/dice/dice3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Telegram/Resources/art/dice/dice4.svg b/Telegram/Resources/art/dice/dice4.svg
new file mode 100644
index 0000000000..dc4af3c88f
--- /dev/null
+++ b/Telegram/Resources/art/dice/dice4.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Telegram/Resources/art/dice/dice5.svg b/Telegram/Resources/art/dice/dice5.svg
new file mode 100644
index 0000000000..cdd1efd1db
--- /dev/null
+++ b/Telegram/Resources/art/dice/dice5.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Telegram/Resources/art/dice/dice6.svg b/Telegram/Resources/art/dice/dice6.svg
new file mode 100644
index 0000000000..f024e88b05
--- /dev/null
+++ b/Telegram/Resources/art/dice/dice6.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Telegram/Resources/icons/chat/mini_roll.svg b/Telegram/Resources/icons/chat/mini_roll.svg
new file mode 100644
index 0000000000..158f3d10b8
--- /dev/null
+++ b/Telegram/Resources/icons/chat/mini_roll.svg
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc
index 6c7e4c94c7..f448e9e4a5 100644
--- a/Telegram/Resources/qrc/telegram/animations.qrc
+++ b/Telegram/Resources/qrc/telegram/animations.qrc
@@ -66,12 +66,6 @@
../../animations/dice/slot_back.tgs
../../animations/dice/slot_pull.tgs
../../animations/dice/winners.tgs
-
- ../../animations/dice/dice_1.tgs
- ../../animations/dice/dice_2.tgs
- ../../animations/dice/dice_3.tgs
- ../../animations/dice/dice_4.tgs
- ../../animations/dice/dice_5.tgs
../../animations/dice/dice_6.tgs
../../animations/star_reaction/appear.tgs
diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc
index e439a4e012..ca37cc179a 100644
--- a/Telegram/Resources/qrc/telegram/telegram.qrc
+++ b/Telegram/Resources/qrc/telegram/telegram.qrc
@@ -36,6 +36,12 @@
../../art/topic_icons/general.svg
../../icons/info/edit/links_subscription.svg
../../icons/plane_white.svg
+ ../../art/dice/dice1.svg
+ ../../art/dice/dice2.svg
+ ../../art/dice/dice3.svg
+ ../../art/dice/dice4.svg
+ ../../art/dice/dice5.svg
+ ../../art/dice/dice6.svg
../../icons/calls/hands.lottie
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp b/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp
index 72279f5964..71c0d033e5 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp
+++ b/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp
@@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/controls/history_view_suggest_options.h"
-#include "base/event_filter.h"
#include "base/unixtime.h"
#include "boxes/star_gift_box.h"
#include "chat_helpers/compose/compose_show.h"
@@ -21,7 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h"
#include "history/history_item_components.h"
#include "history/history_item_helpers.h"
-#include "info/channel_statistics/earn/earn_format.h"
#include "info/channel_statistics/earn/earn_icons.h"
#include "lang/lang_keys.h"
#include "lottie/lottie_icon.h"
@@ -111,36 +109,6 @@ void ChooseSuggestTimeBox(
});
}
-void AddApproximateUsd(
- not_null field,
- not_null session,
- rpl::producer price) {
- auto value = std::move(price) | rpl::map([=](CreditsAmount amount) {
- if (!amount) {
- return QString();
- }
- const auto appConfig = &session->appConfig();
- const auto rate = amount.ton()
- ? appConfig->currencySellRate()
- : (appConfig->starsSellRate() / 100.);
- return Info::ChannelEarn::ToUsd(amount, rate, 2);
- });
- const auto usd = Ui::CreateChild(
- field,
- std::move(value),
- st::suggestPriceEstimate);
- const auto move = [=] {
- usd->moveToRight(0, st::suggestPriceEstimateTop);
- };
- base::install_event_filter(field, [=](not_null e) {
- if (e->type() == QEvent::Resize) {
- move();
- }
- return base::EventFilterResult::Continue;
- });
- usd->widthValue() | rpl::on_next(move, usd->lifetime());
-}
-
StarsTonPriceInput AddStarsTonPriceInput(
not_null container,
StarsTonPriceArgs &&args) {
diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp
index 63c70efb24..1dcb93feb4 100644
--- a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/chat/message_bubble.h"
#include "ui/chat/chat_style.h"
#include "ui/effects/reaction_fly_animation.h"
+#include "ui/text/custom_emoji_helper.h"
#include "ui/text/format_values.h"
#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
@@ -473,6 +474,8 @@ void BottomInfo::layoutDateText() {
: name.isEmpty()
? date
: (name + afterAuthor);
+ auto helper = Ui::Text::CustomEmojiHelper(
+ Core::TextContext({ .session = &_reactionsOwner->session() }));
auto marked = TextWithEntities();
if (const auto count = _data.stars) {
marked.append(
@@ -480,16 +483,24 @@ void BottomInfo::layoutDateText() {
).append(Lang::FormatCountToShort(count).string).append(u", "_q);
}
if (const auto stake = _data.tonStake) {
- marked.append(QString::number(stake / 1e9)
- + QString::fromUtf8("\xf0\x9f\x92\x8e")
- + " ");
+ marked.append(
+ QString::number(stake / 1e9)
+ ).append(helper.image({
+ .image = Ui::Emoji::SinglePixmap(
+ Ui::Emoji::Find(QString::fromUtf8("\xf0\x9f\x92\x8e")),
+ Ui::Emoji::GetSizeNormal()).toImage().scaledToHeight(
+ st::stakeIconEmojiSize * style::DevicePixelRatio(),
+ Qt::SmoothTransformation),
+ .margin = QMargins(0, st::stakeIconEmojiTop, 0, 0),
+ .textColor = false,
+ })).append(" ");
}
marked.append(full);
_authorEditedDate.setMarkedText(
st::msgDateTextStyle,
marked,
Ui::NameTextOptions(),
- Core::TextContext({ .session = &_reactionsOwner->session() }));
+ helper.context());
}
void BottomInfo::layoutViewsText() {
diff --git a/Telegram/SourceFiles/main/main_app_config.cpp b/Telegram/SourceFiles/main/main_app_config.cpp
index 944538aba4..469cb33e50 100644
--- a/Telegram/SourceFiles/main/main_app_config.cpp
+++ b/Telegram/SourceFiles/main/main_app_config.cpp
@@ -280,6 +280,27 @@ bool AppConfig::settingsDisplayPasskeys() const {
return get(u"settings_display_passkeys"_q, false);
}
+int64 AppConfig::stakeDiceNanoTonMin() const {
+ return get(u"ton_stakedice_stake_amount_min"_q, 100'000'000LL);
+}
+
+int64 AppConfig::stakeDiceNanoTonMax() const {
+ return get(u"ton_stakedice_stake_amount_max"_q, 50'000'000'000LL);
+}
+
+std::vector AppConfig::stakeDiceNanoTonSuggested() const {
+ return get>(
+ u"ton_stakedice_stake_suggested_amounts"_q,
+ std::vector{
+ 100'000'000LL,
+ 1'000'000'000LL,
+ 2'000'000'000LL,
+ 5'000'000'000LL,
+ 10'000'000'000LL,
+ 20'000'000'000LL,
+ });
+}
+
void AppConfig::refresh(bool force) {
if (_requestId || !_api) {
if (force) {
@@ -464,6 +485,27 @@ std::vector AppConfig::getIntArray(
});
}
+std::vector AppConfig::getInt64Array(
+ const QString &key,
+ std::vector &&fallback) const {
+ return getValue(key, [&](const MTPJSONValue &value) {
+ return value.match([&](const MTPDjsonArray &data) {
+ auto result = std::vector();
+ result.reserve(data.vvalue().v.size());
+ for (const auto &entry : data.vvalue().v) {
+ if (entry.type() != mtpc_jsonNumber) {
+ return std::move(fallback);
+ }
+ result.push_back(
+ int64(base::SafeRound(entry.c_jsonNumber().vvalue().v)));
+ }
+ return result;
+ }, [&](const auto &data) {
+ return std::move(fallback);
+ });
+ });
+}
+
bool AppConfig::newRequirePremiumFree() const {
return get(
u"new_noncontact_peers_require_premium_without_ownpremium"_q,
diff --git a/Telegram/SourceFiles/main/main_app_config.h b/Telegram/SourceFiles/main/main_app_config.h
index 500d8f57f6..5e9cc97659 100644
--- a/Telegram/SourceFiles/main/main_app_config.h
+++ b/Telegram/SourceFiles/main/main_app_config.h
@@ -47,6 +47,8 @@ public:
return getStringMap(key, std::move(fallback));
} else if constexpr (std::is_same_v>) {
return getIntArray(key, std::move(fallback));
+ } else if constexpr (std::is_same_v>) {
+ return getInt64Array(key, std::move(fallback));
} else if constexpr (std::is_same_v) {
return getBool(key, fallback);
}
@@ -128,6 +130,10 @@ public:
[[nodiscard]] int passkeysAccountPasskeysMax() const;
[[nodiscard]] bool settingsDisplayPasskeys() const;
+ [[nodiscard]] int64 stakeDiceNanoTonMin() const;
+ [[nodiscard]] int64 stakeDiceNanoTonMax() const;
+ [[nodiscard]] std::vector stakeDiceNanoTonSuggested() const;
+
using StarsColoring = Calls::Group::Ui::StarsColoring;
[[nodiscard]] std::vector groupCallColorings() const;
@@ -159,6 +165,9 @@ private:
[[nodiscard]] std::vector getIntArray(
const QString &key,
std::vector &&fallback) const;
+ [[nodiscard]] std::vector getInt64Array(
+ const QString &key,
+ std::vector &&fallback) const;
void updateIgnoredRestrictionReasons(std::vector was);
diff --git a/Telegram/SourceFiles/ui/boxes/emoji_stake_box.cpp b/Telegram/SourceFiles/ui/boxes/emoji_stake_box.cpp
index 7f64486d3a..8db9e85fab 100644
--- a/Telegram/SourceFiles/ui/boxes/emoji_stake_box.cpp
+++ b/Telegram/SourceFiles/ui/boxes/emoji_stake_box.cpp
@@ -7,15 +7,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/boxes/emoji_stake_box.h"
+#include "base/event_filter.h"
#include "base/object_ptr.h"
#include "chat_helpers/stickers_lottie.h"
#include "data/components/credits.h"
#include "data/stickers/data_custom_emoji.h"
#include "data/data_document.h"
#include "data/data_session.h"
+#include "info/channel_statistics/earn/earn_format.h"
#include "info/channel_statistics/earn/earn_icons.h"
#include "lang/lang_keys.h"
#include "lottie/lottie_icon.h"
+#include "main/main_app_config.h"
#include "main/main_session.h"
#include "settings/settings_common.h" // CreateLottieIcon
#include "settings/settings_credits_graphics.h" // AddBalanceWidget
@@ -39,6 +42,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_settings.h" // settingsCloudPasswordIconSize
#include "styles/style_widgets.h"
+#include
+
namespace Ui {
namespace {
@@ -55,12 +60,30 @@ namespace {
helper.context());
}
+[[nodiscard]] QImage MakeEmojiFrame(int index, int size) {
+ const auto path = u":/gui/dice/dice%1.svg"_q.arg(index);
+
+ const auto ratio = style::DevicePixelRatio();
+ auto svg = QSvgRenderer(path);
+ auto result = QImage(
+ QSize(size, size) * ratio,
+ QImage::Format_ARGB32_Premultiplied);
+ result.setDevicePixelRatio(ratio);
+ result.fill(Qt::transparent);
+
+ auto p = QPainter(&result);
+ svg.render(&p, QRect(0, 0, size, size));
+ p.end();
+
+ return result;
+}
+
[[nodiscard]] object_ptr MakeLogo(not_null box) {
const auto &size = st::settingsCloudPasswordIconSize;
auto icon = Settings::CreateLottieIcon(
box->verticalLayout(),
{ .name = u"dice_6"_q, .sizeOverride = { size, size } },
- st::settingLocalPasscodeIconPadding);
+ QMargins());
const auto animate = std::move(icon.animate);
box->showFinishes() | rpl::take(1) | rpl::on_next([=] {
animate(anim::repeat::once);
@@ -75,19 +98,17 @@ namespace {
const auto raw = result.data();
struct State {
- std::array, 6> dices;
+ std::array dices;
std::array multiplicators;
};
const auto state = raw->lifetime().make_state();
const auto serialize = [&](int milliReward) {
return QString(QChar(0xD7)) + QString::number(milliReward / 1000.);
};
- const auto esize = st::largeEmojiSize;
+ const auto esize = st::stakeEmojiSize;
for (auto i = 0; i != 6; ++i) {
- state->dices[i] = Lottie::MakeEmoji({
- .name = u"dice_%1"_q.arg(i + 1),
- .sizeOverride = QSize(esize, esize),
- }, [=] { raw->update(); });
+ state->dices[i] = MakeEmojiFrame(i + 1, esize);
+
const auto value = (i < args.milliRewards.size())
? args.milliRewards[i]
: 0;
@@ -137,13 +158,12 @@ namespace {
auto hq = PainterHighQualityEnabler(p);
p.drawPath(path);
- const auto etop = -style::ConvertScale(10);
+ const auto etop = st::stakeEmojiTop;
const auto ttop = etop + esize;
{
auto left = border + half;
const auto width = (inner.width() - 3 * border) / 4.;
auto top = border + half;
- const auto now = crl::now();
for (auto i = 0; i != 7; ++i, left += width + border) {
if (i == 4) {
left -= 4 * (width + border);
@@ -157,25 +177,17 @@ namespace {
const auto w = (right - x);
if (i < 6) {
- state->dices[i]->paint(p, {
- .textColor = st::windowBoldFg->c,
- .now = now,
- .position = QPoint(x + (w - esize) / 2, y + etop),
- .paused = true,
- .internal = { .forceLastFrame = true }
- });
+ p.drawImage(
+ x + (w - esize) / 2,
+ y + etop,
+ state->dices[i]);
} else {
- const auto e = state->dices.back().get();
+ const auto &last = state->dices.back();
for (auto j = 0; j != 3; ++j) {
- e->paint(p, {
- .textColor = st::windowBoldFg->c,
- .now = now,
- .position = QPoint(
- x + (w - 3 * esize) / 2 + (esize * j),
- y + etop),
- .paused = true,
- .internal = { .forceLastFrame = true }
- });
+ p.drawImage(
+ x + (w - 3 * esize) / 2 + (esize * j),
+ y + etop,
+ last);
}
}
@@ -249,6 +261,36 @@ not_null AddTonInputField(
return result;
}
+void AddApproximateUsd(
+ not_null field,
+ not_null session,
+ rpl::producer price) {
+ auto value = std::move(price) | rpl::map([=](CreditsAmount amount) {
+ if (!amount) {
+ return QString();
+ }
+ const auto appConfig = &session->appConfig();
+ const auto rate = amount.ton()
+ ? appConfig->currencySellRate()
+ : (appConfig->starsSellRate() / 100.);
+ return Info::ChannelEarn::ToUsd(amount, rate, 2);
+ });
+ const auto usd = Ui::CreateChild(
+ field,
+ std::move(value),
+ st::suggestPriceEstimate);
+ const auto move = [=] {
+ usd->moveToRight(0, st::suggestPriceEstimateTop);
+ };
+ base::install_event_filter(field, [=](not_null e) {
+ if (e->type() == QEvent::Resize) {
+ move();
+ }
+ return base::EventFilterResult::Continue;
+ });
+ usd->widthValue() | rpl::on_next(move, usd->lifetime());
+}
+
void EmojiGameStakeBox(
not_null box,
EmojiGameStakeArgs &&args) {
@@ -268,6 +310,10 @@ void EmojiGameStakeBox(
Text::CustomEmojiTextBadge(
tr::lng_stake_game_beta(tr::now),
st::customEmojiTextBadge));
+ const auto sixText = helper.image({
+ .image = MakeEmojiFrame(6, st::emojiSize),
+ .textColor = false,
+ });
auto title = tr::lng_stake_game_title(
tr::marked
) | rpl::map([=](TextWithEntities &&text) {
@@ -304,12 +350,7 @@ void EmojiGameStakeBox(
st::boxRowPadding + QMargins(-half, 0, -half, skip / 2),
style::al_top);
- const auto six = Lottie::IconDescriptor{
- .name = u"dice_6"_q,
- .sizeOverride = { st::emojiSize, st::emojiSize },
- };
- const auto sixText = Text::LottieEmoji(six);
- const auto sixContext = Text::LottieEmojiContext(six);
+ const auto sixContext = helper.context();
const auto factory = [=](
QStringView data,
const Text::MarkedContext &context) {
@@ -352,15 +393,35 @@ void EmojiGameStakeBox(
field->setFocusFast();
});
+ const auto price = field->lifetime().make_state>(
+ args.currentStake);
+ auto priceValue = price->value() | rpl::map([=](int64 amount) {
+ return CreditsAmount(
+ amount / Ui::kNanosInOne,
+ amount % Ui::kNanosInOne,
+ CreditsType::Ton);
+ });
+ AddApproximateUsd(
+ field,
+ args.session,
+ std::move(priceValue));
+
+ field->changes() | rpl::on_next([=] {
+ *price = Ui::ParseTonAmountString(field->getLastText()).value_or(0);
+ }, field->lifetime());
+
const auto submit = args.submit;
const auto callback = [=] {
const auto text = field->getLastText();
- const auto now = Ui::ParseTonAmountString(text);
- if (!now) {
+ const auto now = Ui::ParseTonAmountString(text).value_or(0);
+ const auto appConfig = &args.session->appConfig();
+ const auto min = appConfig->stakeDiceNanoTonMin();
+ const auto max = appConfig->stakeDiceNanoTonMax();
+ if (!now || now < min || now > max) {
field->showError();
} else {
box->closeBox();
- submit(*now);
+ submit(now);
}
};
field->submits() | rpl::on_next(callback, field->lifetime());
@@ -368,7 +429,8 @@ void EmojiGameStakeBox(
button->setText(tr::lng_stake_game_save_and_roll(
) | rpl::map([=](QString text) {
- return tr::marked(
+ return Text::IconEmoji(
+ &st::stakeEmojiIcon,
QString::fromUtf8("\xf0\x9f\x8e\xb2")
).append(' ').append(text);
}));
diff --git a/Telegram/SourceFiles/ui/boxes/emoji_stake_box.h b/Telegram/SourceFiles/ui/boxes/emoji_stake_box.h
index 3b5133582b..3a84abf8ed 100644
--- a/Telegram/SourceFiles/ui/boxes/emoji_stake_box.h
+++ b/Telegram/SourceFiles/ui/boxes/emoji_stake_box.h
@@ -34,6 +34,11 @@ struct TonInputFieldArgs {
not_null container,
TonInputFieldArgs &&args);
+void AddApproximateUsd(
+ not_null field,
+ not_null session,
+ rpl::producer price);
+
struct EmojiGameStakeArgs {
not_null session;
int64 currentStake = 0;
diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style
index 8571434c27..2da01898ef 100644
--- a/Telegram/SourceFiles/ui/chat/chat.style
+++ b/Telegram/SourceFiles/ui/chat/chat.style
@@ -1459,3 +1459,12 @@ messageGiftIconSkip: 30px;
newChatIcon: icon{{ "chat/new_topic", windowFgActive }};
newChatIconPadding: margins(2px, 2px, 2px, 2px);
+
+stakeEmojiSize: 20px;
+stakeEmojiTop: 8px;
+stakeEmojiIcon: IconEmoji {
+ icon: icon{{ "chat/mini_roll-18x18", activeButtonFg }};
+ padding: margins(1px, 2px, 1px, 0px);
+}
+stakeIconEmojiSize: 12px;
+stakeIconEmojiTop: 3px;