mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-07 15:43:55 +02:00
Allow placing your gifts on sale.
This commit is contained in:
parent
5b71281ec4
commit
0a92e12a62
14 changed files with 265 additions and 9 deletions
BIN
Telegram/Resources/icons/menu/tag_sell.png
Normal file
BIN
Telegram/Resources/icons/menu/tag_sell.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 377 B |
BIN
Telegram/Resources/icons/menu/tag_sell@2x.png
Normal file
BIN
Telegram/Resources/icons/menu/tag_sell@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 675 B |
BIN
Telegram/Resources/icons/menu/tag_sell@3x.png
Normal file
BIN
Telegram/Resources/icons/menu/tag_sell@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 930 B |
|
@ -3422,6 +3422,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_gift_stars_limited" = "limited";
|
||||
"lng_gift_stars_sold_out" = "sold out";
|
||||
"lng_gift_stars_resale" = "resale";
|
||||
"lng_gift_stars_on_sale" = "on sale";
|
||||
"lng_gift_stars_tabs_all" = "All Gifts";
|
||||
"lng_gift_stars_tabs_my" = "My Gifts";
|
||||
"lng_gift_stars_tabs_limited" = "Limited";
|
||||
|
@ -3581,6 +3582,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_gift_transfer_button_for" = "Transfer for {price}";
|
||||
"lng_gift_transfer_wear" = "Wear";
|
||||
"lng_gift_transfer_take_off" = "Take Off";
|
||||
"lng_gift_transfer_sell" = "Sell";
|
||||
"lng_gift_transfer_unlist" = "Unlist";
|
||||
"lng_gift_sell_unlist_title" = "Unlist {name}";
|
||||
"lng_gift_sell_unlist_sure" = "Are you sure you want to unlist your gift?";
|
||||
"lng_gift_sell_title" = "Price in Stars";
|
||||
"lng_gift_sell_placeholder" = "Enter price in Stars";
|
||||
"lng_gift_sell_about" = "You will receive {percent} of the selected amount.";
|
||||
"lng_gift_sell_amount#one" = "You will receive **{count}** Star.";
|
||||
"lng_gift_sell_amount#other" = "You will receive **{count}** Stars.";
|
||||
"lng_gift_sell_min_price#one" = "Minimum price is {count} Star.";
|
||||
"lng_gift_sell_min_price#other" = "Minimum price is {count} Stars.";
|
||||
"lng_gift_sell_put" = "Put for Sale";
|
||||
"lng_gift_sell_update" = "Update the Price";
|
||||
"lng_gift_sell_toast" = "{name} is now for sale!";
|
||||
"lng_gift_sell_removed" = "{name} is removed from sale.";
|
||||
"lng_gift_menu_show" = "Show";
|
||||
"lng_gift_menu_hide" = "Hide";
|
||||
"lng_gift_wear_title" = "Wear {name}";
|
||||
|
|
|
@ -145,6 +145,10 @@ void EditPriceBox(
|
|||
field->resize(width, field->height());
|
||||
wrap->resize(width, field->height());
|
||||
}, wrap->lifetime());
|
||||
field->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||
auto p = QPainter(field);
|
||||
st::paidStarIcon.paint(p, 0, st::paidStarIconTop, field->width());
|
||||
}, field->lifetime());
|
||||
field->selectAll();
|
||||
box->setFocusCallback([=] {
|
||||
field->setFocusFast();
|
||||
|
@ -165,11 +169,6 @@ void EditPriceBox(
|
|||
return false;
|
||||
});
|
||||
|
||||
field->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||
auto p = QPainter(field);
|
||||
st::paidStarIcon.paint(p, 0, st::paidStarIconTop, field->width());
|
||||
}, field->lifetime());
|
||||
|
||||
const auto save = [=] {
|
||||
const auto now = field->getLastText().toULongLong();
|
||||
if (now > limit) {
|
||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_global_privacy.h"
|
||||
#include "api/api_premium.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "base/qt_signal_producer.h"
|
||||
#include "base/random.h"
|
||||
#include "base/timer_rpl.h"
|
||||
#include "base/unixtime.h"
|
||||
|
@ -83,6 +84,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/ui_utility.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "ui/widgets/fields/number_input.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
|
@ -4059,6 +4061,149 @@ void ShowUniqueGiftWearBox(
|
|||
}));
|
||||
}
|
||||
|
||||
void ShowUniqueGiftSellBox(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
const Data::UniqueGift &gift,
|
||||
Data::SavedStarGiftId savedId,
|
||||
Settings::GiftWearBoxStyleOverride st) {
|
||||
const auto priceNow = gift.starsForResale;
|
||||
const auto name = Data::UniqueGiftName(gift);
|
||||
const auto slug = gift.slug;
|
||||
show->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->setTitle(tr::lng_gift_sell_title());
|
||||
box->setStyle(st.box ? *st.box : st::upgradeGiftBox);
|
||||
box->setWidth(st::boxWideWidth);
|
||||
|
||||
box->addTopButton(st.close ? *st.close : st::boxTitleClose, [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
|
||||
const auto session = &show->session();
|
||||
AddSubsectionTitle(
|
||||
box->verticalLayout(),
|
||||
tr::lng_gift_sell_placeholder(),
|
||||
(st::boxRowPadding - QMargins(
|
||||
st::defaultSubsectionTitlePadding.left(),
|
||||
0,
|
||||
st::defaultSubsectionTitlePadding.right(),
|
||||
st::defaultSubsectionTitlePadding.bottom())));
|
||||
const auto &appConfig = session->appConfig();
|
||||
const auto limit = appConfig.giftResalePriceMax();
|
||||
const auto minimal = appConfig.giftResalePriceMin();
|
||||
const auto thousandths = appConfig.giftResaleReceiveThousandths();
|
||||
const auto wrap = box->addRow(object_ptr<Ui::FixedHeightWidget>(
|
||||
box,
|
||||
st::editTagField.heightMin));
|
||||
auto owned = object_ptr<Ui::NumberInput>(
|
||||
wrap,
|
||||
st::editTagField,
|
||||
rpl::single(QString()),
|
||||
QString::number(priceNow ? priceNow : minimal),
|
||||
limit);
|
||||
const auto field = owned.data();
|
||||
wrap->widthValue() | rpl::start_with_next([=](int width) {
|
||||
field->move(0, 0);
|
||||
field->resize(width, field->height());
|
||||
wrap->resize(width, field->height());
|
||||
}, wrap->lifetime());
|
||||
field->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||
auto p = QPainter(field);
|
||||
st::paidStarIcon.paint(p, 0, st::paidStarIconTop, field->width());
|
||||
}, field->lifetime());
|
||||
field->selectAll();
|
||||
box->setFocusCallback([=] {
|
||||
field->setFocusFast();
|
||||
});
|
||||
|
||||
const auto errors = box->lifetime().make_state<
|
||||
rpl::event_stream<>
|
||||
>();
|
||||
auto goods = rpl::merge(
|
||||
rpl::single(rpl::empty) | rpl::map_to(true),
|
||||
base::qt_signal_producer(
|
||||
field,
|
||||
&Ui::NumberInput::changed
|
||||
) | rpl::map_to(true),
|
||||
errors->events() | rpl::map_to(false)
|
||||
) | rpl::start_spawning(box->lifetime());
|
||||
auto text = rpl::duplicate(goods) | rpl::map([=](bool good) {
|
||||
const auto value = field->getLastText().toInt();
|
||||
const auto receive = (int64(value) * thousandths) / 1000;
|
||||
return !good
|
||||
? tr::lng_gift_sell_min_price(
|
||||
tr::now,
|
||||
lt_count,
|
||||
minimal,
|
||||
Ui::Text::RichLangValue)
|
||||
: (value >= minimal)
|
||||
? tr::lng_gift_sell_amount(
|
||||
tr::now,
|
||||
lt_count,
|
||||
receive,
|
||||
Ui::Text::RichLangValue)
|
||||
: tr::lng_gift_sell_about(
|
||||
tr::now,
|
||||
lt_percent,
|
||||
TextWithEntities{ u"%1%"_q.arg(thousandths / 10.) },
|
||||
Ui::Text::RichLangValue);
|
||||
});
|
||||
const auto details = box->addRow(object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
std::move(text) | rpl::after_next([=] {
|
||||
box->verticalLayout()->resizeToWidth(box->width());
|
||||
}),
|
||||
st::boxLabel));
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
|
||||
rpl::duplicate(goods) | rpl::start_with_next([=](bool good) {
|
||||
details->setTextColorOverride(
|
||||
good ? st::windowSubTextFg->c : st::boxTextFgError->c);
|
||||
}, details->lifetime());
|
||||
|
||||
const auto button = box->addButton(priceNow
|
||||
? tr::lng_gift_sell_update()
|
||||
: tr::lng_gift_sell_put(), [=] {
|
||||
const auto count = field->getLastText().toInt();
|
||||
if (count < minimal) {
|
||||
field->showError();
|
||||
errors->fire({});
|
||||
return;
|
||||
}
|
||||
box->closeBox();
|
||||
session->api().request(MTPpayments_UpdateStarGiftPrice(
|
||||
(savedId
|
||||
? Api::InputSavedStarGiftId(savedId)
|
||||
: MTP_inputSavedStarGiftSlug(MTP_string(slug))),
|
||||
MTP_long(count)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
session->api().applyUpdates(result);
|
||||
show->showToast(tr::lng_gift_sell_toast(
|
||||
tr::now,
|
||||
lt_name,
|
||||
name));
|
||||
session->data().notifyGiftUpdate({
|
||||
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
show->showToast(error.type());
|
||||
}).send();
|
||||
|
||||
});
|
||||
rpl::combine(
|
||||
box->widthValue(),
|
||||
button->widthValue()
|
||||
) | rpl::start_with_next([=](int outer, int inner) {
|
||||
const auto padding = st::giftBox.buttonPadding;
|
||||
const auto wanted = outer - padding.left() - padding.right();
|
||||
if (inner != wanted) {
|
||||
button->resizeToWidth(wanted);
|
||||
button->moveToLeft(padding.left(), padding.top());
|
||||
}
|
||||
}, box->lifetime());
|
||||
}));
|
||||
}
|
||||
|
||||
struct UpgradeArgs : StarGiftUpgradeArgs {
|
||||
std::vector<Data::UniqueGiftModel> models;
|
||||
std::vector<Data::UniqueGiftPattern> patterns;
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Data {
|
|||
struct UniqueGift;
|
||||
struct GiftCode;
|
||||
struct CreditsHistoryEntry;
|
||||
class SavedStarGiftId;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
|
@ -68,6 +69,13 @@ void ShowUniqueGiftWearBox(
|
|||
const Data::UniqueGift &gift,
|
||||
Settings::GiftWearBoxStyleOverride st);
|
||||
|
||||
void ShowUniqueGiftSellBox(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
const Data::UniqueGift &gift,
|
||||
Data::SavedStarGiftId savedId,
|
||||
Settings::GiftWearBoxStyleOverride st);
|
||||
|
||||
struct PatternPoint {
|
||||
QPointF position;
|
||||
float64 scale = 1.;
|
||||
|
|
|
@ -221,6 +221,8 @@ darkGiftShare: icon {{ "menu/share", groupCallMembersFg }};
|
|||
darkGiftTransfer: icon {{ "chat/input_replace", groupCallMembersFg }};
|
||||
darkGiftNftWear: icon {{ "menu/nft_wear", groupCallMembersFg }};
|
||||
darkGiftNftTakeOff: icon {{ "menu/nft_takeoff", groupCallMembersFg }};
|
||||
darkGiftNftResell: icon {{ "menu/tag_sell", groupCallMembersFg }};
|
||||
darkGiftNftUnlist: icon {{ "menu/tag_remove", groupCallMembersFg }};
|
||||
darkGiftHide: icon {{ "menu/stealth", groupCallMembersFg }};
|
||||
darkGiftShow: icon {{ "menu/show_in_chat", groupCallMembersFg }};
|
||||
darkGiftPin: icon {{ "menu/pin", groupCallMembersFg }};
|
||||
|
@ -262,3 +264,10 @@ darkGiftTableMessage: FlatLabel(giveawayGiftMessage) {
|
|||
darkGiftCodeLink: FlatLabel(giveawayGiftCodeLink) {
|
||||
textFg: mediaviewMenuFg;
|
||||
}
|
||||
darkGiftBoxClose: IconButton(boxTitleClose) {
|
||||
icon: icon {{ "box_button_close", groupCallMemberInactiveIcon }};
|
||||
iconOver: icon {{ "box_button_close", groupCallMemberInactiveIcon }};
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: groupCallMembersBgOver;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -502,7 +502,9 @@ void GiftButton::paintEvent(QPaintEvent *e) {
|
|||
&& !data.userpic
|
||||
&& !data.info.limitedLeft;
|
||||
return GiftBadge{
|
||||
.text = ((unique && (data.resale || pinned))
|
||||
.text = ((unique && data.resale && _small)
|
||||
? tr::lng_gift_stars_on_sale(tr::now)
|
||||
: (unique && (data.resale || pinned))
|
||||
? ('#' + QString::number(unique->number))
|
||||
: data.resale
|
||||
? tr::lng_gift_stars_resale(tr::now)
|
||||
|
@ -518,17 +520,25 @@ void GiftButton::paintEvent(QPaintEvent *e) {
|
|||
(((count % 1000) && (count < 10'000))
|
||||
? Lang::FormatCountDecimal(count)
|
||||
: Lang::FormatCountToShort(count).string))),
|
||||
.bg1 = (unique
|
||||
.bg1 = ((unique && data.resale && _small)
|
||||
? st::boxTextFgGood->c
|
||||
: unique
|
||||
? unique->backdrop.edgeColor
|
||||
: data.resale
|
||||
? st::boxTextFgGood->c
|
||||
: soldOut
|
||||
? st::attentionButtonFg->c
|
||||
: st::windowActiveTextFg->c),
|
||||
.bg2 = (unique
|
||||
.bg2 = ((unique && data.resale && _small)
|
||||
? QColor(0, 0, 0, 0)
|
||||
: unique
|
||||
? unique->backdrop.patternColor
|
||||
: QColor(0, 0, 0, 0)),
|
||||
.fg = unique ? QColor(255, 255, 255) : st::windowBg->c,
|
||||
.fg = ((unique && data.resale && _small)
|
||||
? st::windowBg->c
|
||||
: unique
|
||||
? QColor(255, 255, 255)
|
||||
: st::windowBg->c),
|
||||
.small = true,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -128,6 +128,18 @@ bool AppConfig::confcallPrioritizeVP8() const {
|
|||
return get<bool>(u"confcall_use_vp8"_q, false);
|
||||
}
|
||||
|
||||
int AppConfig::giftResalePriceMax() const {
|
||||
return get<int>(u"stars_stargift_resale_amount_max"_q, 35000);
|
||||
}
|
||||
|
||||
int AppConfig::giftResalePriceMin() const {
|
||||
return get<int>(u"stars_stargift_resale_amount_min"_q, 125);
|
||||
}
|
||||
|
||||
int AppConfig::giftResaleReceiveThousandths() const {
|
||||
return get<int>(u"stars_stargift_resale_commission_permille"_q, 800);
|
||||
}
|
||||
|
||||
void AppConfig::refresh(bool force) {
|
||||
if (_requestId || !_api) {
|
||||
if (force) {
|
||||
|
|
|
@ -83,6 +83,10 @@ public:
|
|||
[[nodiscard]] int confcallSizeLimit() const;
|
||||
[[nodiscard]] bool confcallPrioritizeVP8() const;
|
||||
|
||||
[[nodiscard]] int giftResalePriceMax() const;
|
||||
[[nodiscard]] int giftResalePriceMin() const;
|
||||
[[nodiscard]] int giftResaleReceiveThousandths() const;
|
||||
|
||||
void refresh(bool force = false);
|
||||
|
||||
private:
|
||||
|
|
|
@ -1042,11 +1042,56 @@ void FillUniqueGiftMenu(
|
|||
}, st.wear ? st.wear : &st::menuIconNftWear);
|
||||
}
|
||||
}
|
||||
const auto sell = owner->isSelf()
|
||||
? e.in
|
||||
: (owner->isChannel() && owner->asChannel()->canTransferGifts());
|
||||
if (sell) {
|
||||
const auto resalePrice = unique->starsForResale;
|
||||
if (resalePrice > 0) {
|
||||
menu->addAction(tr::lng_gift_transfer_unlist(tr::now), [=] {
|
||||
const auto name = UniqueGiftName(*unique);
|
||||
const auto confirm = [=](Fn<void()> close) {
|
||||
close();
|
||||
session->api().request(MTPpayments_UpdateStarGiftPrice(
|
||||
(savedId
|
||||
? Api::InputSavedStarGiftId(savedId)
|
||||
: MTP_inputSavedStarGiftSlug(
|
||||
MTP_string(unique->slug))),
|
||||
MTP_long(0)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
session->api().applyUpdates(result);
|
||||
show->showToast(tr::lng_gift_sell_removed(
|
||||
tr::now,
|
||||
lt_name,
|
||||
name));
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
show->showToast(error.type());
|
||||
}).send();
|
||||
};
|
||||
show->show(Ui::MakeConfirmBox({
|
||||
.text = tr::lng_gift_sell_unlist_sure(),
|
||||
.confirmed = confirm,
|
||||
.confirmText = tr::lng_gift_transfer_unlist(),
|
||||
.title = tr::lng_gift_sell_unlist_title(
|
||||
lt_name,
|
||||
rpl::single(name)),
|
||||
}));
|
||||
}, st.unlist ? st.unlist : &st::menuIconTagRemove);
|
||||
} else {
|
||||
menu->addAction(tr::lng_gift_transfer_sell(tr::now), [=] {
|
||||
const auto style = st.giftWearBox
|
||||
? *st.giftWearBox
|
||||
: GiftWearBoxStyleOverride();
|
||||
ShowUniqueGiftSellBox(show, owner, *unique, savedId, style);
|
||||
}, st.resell ? st.resell : &st::menuIconTagSell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GiftWearBoxStyleOverride DarkGiftWearBoxStyle() {
|
||||
return {
|
||||
.box = &st::darkUpgradeGiftBox,
|
||||
.close = &st::darkGiftBoxClose,
|
||||
.title = &st::darkUpgradeGiftTitle,
|
||||
.subtitle = &st::darkUpgradeGiftSubtitle,
|
||||
.radiantIcon = &st::darkUpgradeGiftRadiant,
|
||||
|
@ -1068,6 +1113,8 @@ CreditsEntryBoxStyleOverrides DarkCreditsEntryBoxStyle() {
|
|||
.transfer = &st::darkGiftTransfer,
|
||||
.wear = &st::darkGiftNftWear,
|
||||
.takeoff = &st::darkGiftNftTakeOff,
|
||||
.resell = &st::darkGiftNftResell,
|
||||
.unlist = &st::darkGiftNftUnlist,
|
||||
.show = &st::darkGiftShow,
|
||||
.hide = &st::darkGiftHide,
|
||||
.pin = &st::darkGiftPin,
|
||||
|
|
|
@ -46,6 +46,7 @@ struct Box;
|
|||
struct Table;
|
||||
struct FlatLabel;
|
||||
struct PopupMenu;
|
||||
struct IconButton;
|
||||
struct PeerListItem;
|
||||
} // namespace style
|
||||
|
||||
|
@ -94,6 +95,7 @@ void AddWithdrawalWidget(
|
|||
|
||||
struct GiftWearBoxStyleOverride {
|
||||
const style::Box *box = nullptr;
|
||||
const style::IconButton *close = nullptr;
|
||||
const style::FlatLabel *title = nullptr;
|
||||
const style::FlatLabel *subtitle = nullptr;
|
||||
const style::icon *radiantIcon = nullptr;
|
||||
|
@ -114,6 +116,8 @@ struct CreditsEntryBoxStyleOverrides {
|
|||
const style::icon *transfer = nullptr;
|
||||
const style::icon *wear = nullptr;
|
||||
const style::icon *takeoff = nullptr;
|
||||
const style::icon *resell = nullptr;
|
||||
const style::icon *unlist = nullptr;
|
||||
const style::icon *show = nullptr;
|
||||
const style::icon *hide = nullptr;
|
||||
const style::icon *pin = nullptr;
|
||||
|
|
|
@ -159,6 +159,8 @@ menuIconAsTopics: icon {{ "menu/mode_topics", menuIconColor }};
|
|||
menuIconAsMessages: icon {{ "menu/mode_messages", menuIconColor }};
|
||||
menuIconTagFilter: icon{{ "menu/tag_filter", menuIconColor }};
|
||||
menuIconTagRename: icon{{ "menu/tag_rename", menuIconColor }};
|
||||
menuIconTagRemove: icon {{ "menu/tag_remove", menuIconColor }};
|
||||
menuIconTagSell: icon {{ "menu/tag_sell", menuIconColor }};
|
||||
menuIconGroupsHide: icon {{ "menu/hide_members", menuIconColor }};
|
||||
menuIconFont: icon {{ "menu/fonts", menuIconColor }};
|
||||
menuIconFactcheck: icon {{ "menu/factcheck", menuIconColor }};
|
||||
|
|
Loading…
Add table
Reference in a new issue